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#ifdef QT_KEYPAD_NAVIGATION
981 if (view->currentIndex().isValid() && view->currentIndex().flags().testFlag(Qt::ItemIsEnabled)) {
984 emit itemSelected(view->currentIndex());
988 if (!(keyEvent->modifiers() & Qt::AltModifier))
994 emit itemSelected(view->currentIndex());
997#if QT_CONFIG(shortcut)
998 if (keyEvent->matches(QKeySequence::Cancel) && isVisible()) {
1007 case QEvent::MouseMove:
1009 QMouseEvent *m =
static_cast<QMouseEvent *>(e);
1010 QWidget *widget =
static_cast<QWidget *>(o);
1011 QPoint vector = widget->mapToGlobal(m->position().toPoint()) - initialClickPosition;
1012 if (vector.manhattanLength() > 9 && blockMouseReleaseTimer.isActive())
1013 blockMouseReleaseTimer.stop();
1014 if (combo->style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking_Current,
nullptr, combo)) {
1015 QModelIndex indexUnderMouse = view->indexAt(m->position().toPoint());
1016 if (indexUnderMouse.isValid()
1017 && !QComboBoxDelegate::isSeparator(indexUnderMouse)) {
1018 view->setCurrentIndex(indexUnderMouse);
1023 case QEvent::MouseButtonPress:
1024 maybeIgnoreMouseButtonRelease =
false;
1026 case QEvent::MouseButtonRelease: {
1027 bool ignoreEvent = maybeIgnoreMouseButtonRelease && popupTimer.elapsed() < QApplication::doubleClickInterval();
1029 QMouseEvent *m =
static_cast<QMouseEvent *>(e);
1030 if (isVisible() && view->rect().contains(m->position().toPoint()) && view->currentIndex().isValid()
1031 && !blockMouseReleaseTimer.isActive() && !ignoreEvent
1032 && (view->currentIndex().flags().testFlag(Qt::ItemIsEnabled))
1033 && (view->currentIndex().flags().testFlag(Qt::ItemIsSelectable))) {
1035 emit itemSelected(view->currentIndex());
1043 return QFrame::eventFilter(o, e);
1046void QComboBoxPrivateContainer::showEvent(QShowEvent *)
1051void QComboBoxPrivateContainer::hideEvent(QHideEvent *)
1055#if QT_CONFIG(graphicsview)
1059 if (QGraphicsProxyWidget *proxy = graphicsProxyWidget())
1064void QComboBoxPrivateContainer::mousePressEvent(QMouseEvent *e)
1067 QStyleOptionComboBox opt = comboStyleOption();
1068 opt.subControls = QStyle::SC_All;
1069 opt.activeSubControls = QStyle::SC_ComboBoxArrow;
1070 QStyle::SubControl sc = combo->style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt,
1071 combo->mapFromGlobal(e->globalPosition().toPoint()),
1073 if ((combo->isEditable() && sc == QStyle::SC_ComboBoxArrow)
1074 || (!combo->isEditable() && sc != QStyle::SC_None))
1075 setAttribute(Qt::WA_NoMouseReplay);
1079void QComboBoxPrivateContainer::mouseReleaseEvent(QMouseEvent *e)
1082 if (!blockMouseReleaseTimer.isActive()) {
1088QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption()
const
1092 QStyleOptionComboBox opt;
1093 opt.initFrom(combo);
1094 opt.subControls = QStyle::SC_All;
1095 opt.activeSubControls = QStyle::SC_None;
1096 opt.editable = combo->isEditable();
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1128
1129
1130
1131
1132
1133
1134
1135
1138
1139
1140
1141
1142
1143
1144
1145
1146
1149
1150
1151
1152
1153
1156
1157
1158
1159
1160
1161
1164
1165
1166
1167
1168
1169
1170
1171
1174
1175
1176
1177
1178
1179
1180
1181
1182
1185
1186
1187
1188QComboBox::QComboBox(QWidget *parent)
1189 : QWidget(*
new QComboBoxPrivate(), parent, { })
1196
1197
1198QComboBox::QComboBox(QComboBoxPrivate &dd, QWidget *parent)
1199 : QWidget(dd, parent, { })
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
1287
1288
1289
1291void QComboBoxPrivate::init()
1299 if (!q->isEditable())
1300 q->setFocusPolicy(Qt::TabFocus);
1303 q->setFocusPolicy(Qt::WheelFocus);
1305 q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed,
1306 QSizePolicy::ComboBox));
1307 setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
1308 q->setModel(
new QStandardItemModel(0, 1, q));
1309 if (!q->isEditable())
1310 q->setAttribute(Qt::WA_InputMethodEnabled,
false);
1312 q->setAttribute(Qt::WA_InputMethodEnabled);
1315QComboBoxPrivateContainer* QComboBoxPrivate::viewContainer()
1321 container =
new QComboBoxPrivateContainer(
new QComboBoxListView(q), q);
1323 container->itemView()->setModel(model);
1325 container->itemView()->setTextElideMode(Qt::ElideMiddle);
1326 updateDelegate(
true);
1327 updateLayoutDirection();
1328 updateViewContainerPaletteAndOpacity();
1329 QObjectPrivate::connect(container, &QComboBoxPrivateContainer::itemSelected,
1330 this, &QComboBoxPrivate::itemSelected);
1331 QObjectPrivate::connect(container->itemView()->selectionModel(),
1332 &QItemSelectionModel::currentChanged,
1333 this, &QComboBoxPrivate::emitHighlighted);
1334 QObjectPrivate::connect(container, &QComboBoxPrivateContainer::resetButton,
1335 this, &QComboBoxPrivate::resetButton);
1340void QComboBoxPrivate::resetButton()
1342 updateArrow(QStyle::State_None);
1345void QComboBoxPrivate::dataChanged(
const QModelIndex &topLeft,
const QModelIndex &bottomRight)
1348 if (inserting || topLeft.parent() != root)
1351 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1353 adjustComboBoxSize();
1354 q->updateGeometry();
1357 if (currentIndex.row() >= topLeft.row() && currentIndex.row() <= bottomRight.row()) {
1358 const QString text = q->itemText(currentIndex.row());
1360 lineEdit->setText(text);
1361 updateLineEditGeometry();
1363 updateCurrentText(text);
1366#if QT_CONFIG(accessibility)
1367 QAccessibleValueChangeEvent event(q, text);
1368 QAccessible::updateAccessibility(&event);
1373void QComboBoxPrivate::rowsInserted(
const QModelIndex &parent,
int start,
int end)
1376 if (inserting || parent != root)
1379 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1381 adjustComboBoxSize();
1382 q->updateGeometry();
1386 if (start == 0 && (end - start + 1) == q->count() && !currentIndex.isValid() &&
1387 placeholderText.isEmpty()) {
1388#if QT_CONFIG(accessibility)
1393 if (container && container->itemView()) {
1394 QAccessibleTableModelChangeEvent event(container->itemView(),
1395 QAccessibleTableModelChangeEvent::ModelReset);
1396 QAccessible::updateAccessibility(&event);
1399 q->setCurrentIndex(0);
1401 }
else if (currentIndex.row() != indexBeforeChange) {
1403 emitCurrentIndexChanged(currentIndex);
1407void QComboBoxPrivate::updateIndexBeforeChange()
1409 indexBeforeChange = currentIndex.row();
1412void QComboBoxPrivate::rowsRemoved(
const QModelIndex &parent,
int ,
int )
1418 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1420 adjustComboBoxSize();
1421 q->updateGeometry();
1425 if (model->rowCount(root) == 0) {
1426 setCurrentIndex(QModelIndex());
1431 if (currentIndex.row() != indexBeforeChange) {
1432 if (!currentIndex.isValid() && q->count()) {
1433 q->setCurrentIndex(qMin(q->count() - 1, qMax(indexBeforeChange, 0)));
1437 lineEdit->setText(q->itemText(currentIndex.row()));
1438 updateLineEditGeometry();
1441 emitCurrentIndexChanged(currentIndex);
1446void QComboBoxPrivate::updateViewContainerPaletteAndOpacity()
1451 QStyleOptionComboBox opt;
1452 q->initStyleOption(&opt);
1454 if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q)) {
1456 menu.ensurePolished();
1457 container->setPalette(menu.palette());
1458 container->setWindowOpacity(menu.windowOpacity());
1462 container->setPalette(q->palette());
1463 container->setWindowOpacity(1.0);
1466 lineEdit->setPalette(q->palette());
1469void QComboBoxPrivate::updateFocusPolicy()
1475 if (q->isEditable())
1476 q->setFocusPolicy(Qt::WheelFocus);
1478 q->setFocusPolicy(Qt::TabFocus);
1483
1484
1485
1486
1487
1488
1489void QComboBox::initStyleOption(QStyleOptionComboBox *option)
const
1494 Q_D(
const QComboBox);
1495 option->initFrom(
this);
1496 option->editable = isEditable();
1497 option->frame = d->frame;
1498 if (hasFocus() && !option->editable)
1499 option->state |= QStyle::State_Selected;
1500 option->subControls = QStyle::SC_All;
1501 if (d->arrowState == QStyle::State_Sunken) {
1502 option->activeSubControls = QStyle::SC_ComboBoxArrow;
1503 option->state |= d->arrowState;
1505 option->activeSubControls = d->hoverControl;
1507 option->currentText = currentText();
1508 if (d->currentIndex.isValid()) {
1509 option->currentIcon = d->itemIcon(d->currentIndex);
1510 QVariant alignment = d->model->data(d->currentIndex, Qt::TextAlignmentRole);
1511 if (alignment.isValid())
1512 option->textAlignment =
static_cast<Qt::Alignment>(alignment.toUInt());
1514 option->iconSize = iconSize();
1515 if (d->container && d->container->isVisible())
1516 option->state |= QStyle::State_On;
1519void QComboBoxPrivate::initViewItemOption(QStyleOptionViewItem *option)
const
1521 Q_Q(
const QComboBox);
1522 q->view()->initViewItemOption(option);
1524 option->index = currentIndex;
1525 option->text = q->currentText();
1526 option->icon = itemIcon(currentIndex);
1529void QComboBoxPrivate::updateLineEditGeometry()
1535 QStyleOptionComboBox opt;
1536 q->initStyleOption(&opt);
1537 QRect editRect = q->style()->subControlRect(QStyle::CC_ComboBox, &opt,
1538 QStyle::SC_ComboBoxEditField, q);
1539 if (currentIndex.isValid() && !q->itemIcon(q->currentIndex()).isNull()) {
1540 QRect comboRect(editRect);
1541 editRect.setWidth(editRect.width() - q->iconSize().width() - 4);
1542 editRect = QStyle::alignedRect(q->layoutDirection(), Qt::AlignRight,
1543 editRect.size(), comboRect);
1545 lineEdit->setGeometry(editRect);
1548Qt::MatchFlags QComboBoxPrivate::matchFlags()
const
1551 Qt::MatchFlags flags = Qt::MatchFixedString;
1552#if QT_CONFIG(completer)
1553 if (!lineEdit->completer() || lineEdit->completer()->caseSensitivity() == Qt::CaseSensitive)
1555 flags |= Qt::MatchCaseSensitive;
1560void QComboBoxPrivate::editingFinished()
1565 const auto leText = lineEdit->text();
1566 if (!leText.isEmpty() && itemText(currentIndex) != leText) {
1567#if QT_CONFIG(completer)
1568 const auto *leCompleter = lineEdit->completer();
1569 const auto *popup = leCompleter ? QCompleterPrivate::get(leCompleter)->popup :
nullptr;
1570 if (popup && popup->isVisible()) {
1575 const QItemSelectionModel *selModel = popup->selectionModel();
1576 const QModelIndex curIndex = popup->currentIndex();
1577 const bool completerIsActive = selModel && selModel->selectedIndexes().contains(curIndex);
1579 if (completerIsActive)
1583 const int index = q_func()->findText(leText, matchFlags());
1585 q->setCurrentIndex(index);
1586 emitActivated(currentIndex);
1592void QComboBoxPrivate::returnPressed()
1600 if (insertPolicy == QComboBox::NoInsert)
1603 if (lineEdit && !lineEdit->text().isEmpty()) {
1604 if (q->count() >= maxCount && !(
this->insertPolicy == QComboBox::InsertAtCurrent))
1606 lineEdit->deselect();
1607 lineEdit->end(
false);
1608 QString text = lineEdit->text();
1611 if (!duplicatesEnabled) {
1612 index = q->findText(text, matchFlags());
1614 q->setCurrentIndex(index);
1615 emitActivated(currentIndex);
1619 switch (insertPolicy) {
1620 case QComboBox::InsertAtTop:
1623 case QComboBox::InsertAtBottom:
1626 case QComboBox::InsertAtCurrent:
1627 case QComboBox::InsertAfterCurrent:
1628 case QComboBox::InsertBeforeCurrent:
1629 if (!q->count() || !currentIndex.isValid())
1631 else if (insertPolicy == QComboBox::InsertAtCurrent)
1632 q->setItemText(q->currentIndex(), text);
1633 else if (insertPolicy == QComboBox::InsertAfterCurrent)
1634 index = q->currentIndex() + 1;
1635 else if (insertPolicy == QComboBox::InsertBeforeCurrent)
1636 index = q->currentIndex();
1638 case QComboBox::InsertAlphabetically:
1640 for (
int i = 0; i < q->count(); ++i, ++index) {
1641 if (text.toLower() < q->itemText(i).toLower())
1649 q->insertItem(index, text);
1650 q->setCurrentIndex(index);
1651 emitActivated(currentIndex);
1656void QComboBoxPrivate::itemSelected(
const QModelIndex &item)
1659 if (item != currentIndex) {
1660 setCurrentIndex(item);
1661 }
else if (lineEdit) {
1662 lineEdit->selectAll();
1663 lineEdit->setText(q->itemText(currentIndex.row()));
1665 emitActivated(currentIndex);
1668void QComboBoxPrivate::emitActivated(
const QModelIndex &index)
1671 if (!index.isValid())
1673 QString text(itemText(index));
1674 emit q->activated(index.row());
1675 emit q->textActivated(text);
1678void QComboBoxPrivate::emitHighlighted(
const QModelIndex &index)
1681 if (!index.isValid())
1683 QString text(itemText(index));
1684 emit q->highlighted(index.row());
1685 emit q->textHighlighted(text);
1688void QComboBoxPrivate::emitCurrentIndexChanged(
const QModelIndex &index)
1691 const QString text = itemText(index);
1692 emit q->currentIndexChanged(index.row());
1695 updateCurrentText(text);
1696#if QT_CONFIG(accessibility)
1697 QAccessibleValueChangeEvent event(q, text);
1698 QAccessible::updateAccessibility(&event);
1702QString QComboBoxPrivate::itemText(
const QModelIndex &index)
const
1704 return index.isValid() ? model->data(index, itemRole()).toString() : QString();
1707int QComboBoxPrivate::itemRole()
const
1709 return q_func()->isEditable() ? Qt::EditRole : Qt::DisplayRole;
1713
1714
1715QComboBox::~QComboBox()
1721 d->disconnectModel();
1730 d->container->close();
1731 delete d->container;
1732 d->container =
nullptr;
1737
1738
1739
1740
1741
1742
1743
1744
1745int QComboBox::maxVisibleItems()
const
1747 Q_D(
const QComboBox);
1748 return d->maxVisibleItems;
1751void QComboBox::setMaxVisibleItems(
int maxItems)
1754 if (Q_UNLIKELY(maxItems < 0)) {
1755 qWarning(
"QComboBox::setMaxVisibleItems: "
1756 "Invalid max visible items (%d) must be >= 0", maxItems);
1759 d->maxVisibleItems = maxItems;
1763
1764
1765
1766
1767
1768int QComboBox::count()
const
1770 Q_D(
const QComboBox);
1771 return d->model->rowCount(d->root);
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786void QComboBox::setMaxCount(
int max)
1789 if (Q_UNLIKELY(max < 0)) {
1790 qWarning(
"QComboBox::setMaxCount: Invalid count (%d) must be >= 0", max);
1794 const int rowCount = count();
1796 d->model->removeRows(max, rowCount - max, d->root);
1801int QComboBox::maxCount()
const
1803 Q_D(
const QComboBox);
1808
1809
1810
1811
1812
1813
1814
1815
1816bool QComboBox::duplicatesEnabled()
const
1818 Q_D(
const QComboBox);
1819 return d->duplicatesEnabled;
1822void QComboBox::setDuplicatesEnabled(
bool enable)
1825 d->duplicatesEnabled = enable;
1829
1830
1831
1832
1833
1834
1837
1838
1839
1840
1841
1842int QComboBox::findData(
const QVariant &data,
int role, Qt::MatchFlags flags)
const
1844 Q_D(
const QComboBox);
1845 QModelIndex start = d->model->index(0, d->modelColumn, d->root);
1846 const QModelIndexList result = d->model->match(start, role, data, 1, flags);
1847 if (result.isEmpty())
1849 return result.first().row();
1853
1854
1855
1856
1857
1858
1859
1860
1861
1863QComboBox::InsertPolicy QComboBox::insertPolicy()
const
1865 Q_D(
const QComboBox);
1866 return d->insertPolicy;
1869void QComboBox::setInsertPolicy(InsertPolicy policy)
1872 d->insertPolicy = policy;
1876
1877
1878
1879
1880
1881
1882
1883
1885QComboBox::SizeAdjustPolicy QComboBox::sizeAdjustPolicy()
const
1887 Q_D(
const QComboBox);
1888 return d->sizeAdjustPolicy;
1891void QComboBox::setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy policy)
1894 if (policy == d->sizeAdjustPolicy)
1897 d->sizeAdjustPolicy = policy;
1898 d->sizeHint = QSize();
1899 d->adjustComboBoxSize();
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914int QComboBox::minimumContentsLength()
const
1916 Q_D(
const QComboBox);
1917 return d->minimumContentsLength;
1920void QComboBox::setMinimumContentsLength(
int characters)
1923 if (characters == d->minimumContentsLength || characters < 0)
1926 d->minimumContentsLength = characters;
1928 if (d->sizeAdjustPolicy == AdjustToContents
1929 || d->sizeAdjustPolicy == AdjustToMinimumContentsLengthWithIcon) {
1930 d->sizeHint = QSize();
1931 d->adjustComboBoxSize();
1937
1938
1939
1940
1941
1942
1943
1945QSize QComboBox::iconSize()
const
1947 Q_D(
const QComboBox);
1948 if (d->iconSize.isValid())
1951 int iconWidth = style()->pixelMetric(QStyle::PM_SmallIconSize,
nullptr,
this);
1952 return QSize(iconWidth, iconWidth);
1955void QComboBox::setIconSize(
const QSize &size)
1958 if (size == d->iconSize)
1961 view()->setIconSize(size);
1963 d->sizeHint = QSize();
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983void QComboBox::setPlaceholderText(
const QString &placeholderText)
1986 if (placeholderText == d->placeholderText)
1989 d->placeholderText = placeholderText;
1990 if (currentIndex() == -1) {
1991 if (d->placeholderText.isEmpty())
2000QString QComboBox::placeholderText()
const
2002 Q_D(
const QComboBox);
2003 return d->placeholderText;
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018bool QComboBox::isEditable()
const
2020 Q_D(
const QComboBox);
2021 return d->lineEdit !=
nullptr;
2025
2026
2027
2028
2029
2030
2031void QComboBoxPrivate::updateDelegate(
bool force)
2034 QStyleOptionComboBox opt;
2035 q->initStyleOption(&opt);
2036 if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q)) {
2037 if (force || qobject_cast<QComboBoxDelegate *>(q->itemDelegate()))
2038 q->setItemDelegate(
new QComboMenuDelegate(q->view(), q));
2040 if (force || qobject_cast<QComboMenuDelegate *>(q->itemDelegate()))
2041 q->setItemDelegate(
new QComboBoxDelegate(q->view(), q));
2045QIcon QComboBoxPrivate::itemIcon(
const QModelIndex &index)
const
2047 if (!index.isValid())
2049 QVariant decoration = model->data(index, Qt::DecorationRole);
2050 if (decoration.userType() == QMetaType::QPixmap)
2051 return QIcon(qvariant_cast<QPixmap>(decoration));
2053 return qvariant_cast<QIcon>(decoration);
2056void QComboBox::setEditable(
bool editable)
2059 if (isEditable() == editable)
2062 QStyleOptionComboBox opt;
2063 initStyleOption(&opt);
2065 if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this)) {
2066 d->viewContainer()->updateScrollers();
2067 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2069 QLineEdit *le =
new QLineEdit(
this);
2070 le->setPalette(palette());
2073 if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this)) {
2074 d->viewContainer()->updateScrollers();
2075 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2077 setAttribute(Qt::WA_InputMethodEnabled,
false);
2078 d->lineEdit->hide();
2079 d->lineEdit->deleteLater();
2080 d->lineEdit =
nullptr;
2083 d->updateDelegate();
2084 d->updateFocusPolicy();
2086 d->viewContainer()->updateTopBottomMargin();
2087 if (!testAttribute(Qt::WA_Resized))
2092
2093
2094
2095
2096
2097
2098
2099void QComboBox::setLineEdit(QLineEdit *edit)
2102 if (Q_UNLIKELY(!edit)) {
2103 qWarning(
"QComboBox::setLineEdit: cannot set a 0 line edit");
2107 if (edit == d->lineEdit)
2110 edit->setText(currentText());
2115 qt_widget_private(d->lineEdit)->inheritsInputMethodHints = 1;
2117 if (d->lineEdit->parent() !=
this)
2118 d->lineEdit->setParent(
this);
2119 QObjectPrivate::connect(d->lineEdit, &QLineEdit::returnPressed,
2120 d, &QComboBoxPrivate::returnPressed);
2121 QObjectPrivate::connect(d->lineEdit, &QLineEdit::editingFinished,
2122 d, &QComboBoxPrivate::editingFinished);
2123 connect(d->lineEdit, &QLineEdit::textChanged,
this, &QComboBox::editTextChanged);
2124 connect(d->lineEdit, &QLineEdit::textChanged,
this, &QComboBox::currentTextChanged);
2125 QObjectPrivate::connect(d->lineEdit, &QLineEdit::cursorPositionChanged,
2126 d, &QComboBoxPrivate::updateMicroFocus);
2127 QObjectPrivate::connect(d->lineEdit, &QLineEdit::selectionChanged,
2128 d, &QComboBoxPrivate::updateMicroFocus);
2129 QObjectPrivate::connect(d->lineEdit->d_func()->control, &QWidgetLineControl::updateMicroFocus,
2130 d, &QComboBoxPrivate::updateMicroFocus);
2131 d->lineEdit->setFrame(
false);
2132 d->lineEdit->setContextMenuPolicy(Qt::NoContextMenu);
2133 d->updateFocusPolicy();
2134 d->lineEdit->setFocusProxy(
this);
2135 d->lineEdit->setAttribute(Qt::WA_MacShowFocusRect,
false);
2137#if QT_CONFIG(completer)
2139 if (!d->lineEdit->completer()) {
2140 QCompleter *completer =
new QCompleter(d->model, d->lineEdit);
2141 completer->setCaseSensitivity(Qt::CaseInsensitive);
2142 completer->setCompletionMode(QCompleter::InlineCompletion);
2143 completer->setCompletionColumn(d->modelColumn);
2145#ifdef QT_KEYPAD_NAVIGATION
2149 if (QApplicationPrivate::keypadNavigationEnabled())
2150 completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
2153 setCompleter(completer);
2157 setAttribute(Qt::WA_InputMethodEnabled);
2158 d->updateLayoutDirection();
2159 d->updateLineEditGeometry();
2161 d->lineEdit->show();
2167
2168
2169
2170
2171
2172QLineEdit *QComboBox::lineEdit()
const
2174 Q_D(
const QComboBox);
2178#ifndef QT_NO_VALIDATOR
2180
2181
2182
2183
2184
2185
2187void QComboBox::setValidator(
const QValidator *v)
2191 d->lineEdit->setValidator(v);
2195
2196
2197
2198
2199
2200const QValidator *QComboBox::validator()
const
2202 Q_D(
const QComboBox);
2203 return d->lineEdit ? d->lineEdit->validator() :
nullptr;
2207#if QT_CONFIG(completer)
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223void QComboBox::setCompleter(QCompleter *c)
2227 qWarning(
"Setting a QCompleter on non-editable QComboBox is not allowed.");
2230 d->lineEdit->setCompleter(c);
2232 QObjectPrivate::connect(c, QOverload<
const QModelIndex &>::of(&QCompleter::activated),
2233 d, &QComboBoxPrivate::completerActivated);
2239
2240
2241
2242
2243
2244
2245
2246QCompleter *QComboBox::completer()
const
2248 Q_D(
const QComboBox);
2249 return d->lineEdit ? d->lineEdit->completer() :
nullptr;
2255
2256
2257
2258
2259QAbstractItemDelegate *QComboBox::itemDelegate()
const
2261 return view()->itemDelegate();
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279void QComboBox::setItemDelegate(QAbstractItemDelegate *delegate)
2281 if (Q_UNLIKELY(!delegate)) {
2282 qWarning(
"QComboBox::setItemDelegate: cannot set a 0 delegate");
2285 view()->setItemDelegate(delegate);
2289
2290
2292QAbstractItemModel *QComboBox::model()
const
2294 Q_D(
const QComboBox);
2295 if (d->model == QAbstractItemModelPrivate::staticEmptyModel()) {
2296 QComboBox *that =
const_cast<QComboBox*>(
this);
2297 that->setModel(
new QStandardItemModel(0, 1, that));
2303
2304
2305
2306
2307
2308
2309
2310
2311void QComboBox::setModel(QAbstractItemModel *model)
2315 if (Q_UNLIKELY(!model)) {
2316 qWarning(
"QComboBox::setModel: cannot set a 0 model");
2320 if (model == d->model)
2323#if QT_CONFIG(completer)
2324 if (d->lineEdit && d->lineEdit->completer())
2325 d->lineEdit->completer()->setModel(model);
2327 d->disconnectModel();
2328 if (d->model && d->model->QObject::parent() ==
this) {
2336 d->container->itemView()->setModel(model);
2337 QObjectPrivate::connect(d->container->itemView()->selectionModel(),
2338 &QItemSelectionModel::currentChanged,
2339 d, &QComboBoxPrivate::emitHighlighted, Qt::UniqueConnection);
2344 setRootModelIndex(QModelIndex());
2346 d->trySetValidIndex();
2350void QComboBoxPrivate::connectModel()
2355 modelConnections = {
2356 QObjectPrivate::connect(model, &QAbstractItemModel::dataChanged,
2357 this, &QComboBoxPrivate::dataChanged),
2358 QObjectPrivate::connect(model, &QAbstractItemModel::rowsAboutToBeInserted,
2359 this, &QComboBoxPrivate::updateIndexBeforeChange),
2360 QObjectPrivate::connect(model, &QAbstractItemModel::rowsInserted,
2361 this, &QComboBoxPrivate::rowsInserted),
2362 QObjectPrivate::connect(model, &QAbstractItemModel::rowsAboutToBeRemoved,
2363 this, &QComboBoxPrivate::updateIndexBeforeChange),
2364 QObjectPrivate::connect(model, &QAbstractItemModel::rowsRemoved,
2365 this, &QComboBoxPrivate::rowsRemoved),
2366 QObjectPrivate::connect(model, &QObject::destroyed,
2367 this, &QComboBoxPrivate::modelDestroyed),
2368 QObjectPrivate::connect(model, &QAbstractItemModel::modelAboutToBeReset,
2369 this, &QComboBoxPrivate::updateIndexBeforeChange),
2370 QObjectPrivate::connect(model, &QAbstractItemModel::modelReset,
2371 this, &QComboBoxPrivate::modelReset)
2375void QComboBoxPrivate::disconnectModel()
2377 for (
auto &connection : modelConnections)
2378 QObject::disconnect(connection);
2382
2383
2384
2385
2387QModelIndex QComboBox::rootModelIndex()
const
2389 Q_D(
const QComboBox);
2390 return QModelIndex(d->root);
2394
2395
2396
2397
2398void QComboBox::setRootModelIndex(
const QModelIndex &index)
2401 if (d->root == index)
2403 d->root = QPersistentModelIndex(index);
2404 view()->setRootIndex(index);
2409
2410
2411
2412
2413
2414
2415
2416
2417int QComboBox::currentIndex()
const
2419 Q_D(
const QComboBox);
2420 return d->currentIndex.row();
2423void QComboBox::setCurrentIndex(
int index)
2426 QModelIndex mi = index >= 0 ? d->model->index(index, d->modelColumn, d->root) : QModelIndex();
2427 d->setCurrentIndex(mi);
2430void QComboBox::setCurrentText(
const QString &text)
2435 const int i = findText(text);
2441void QComboBoxPrivate::setCurrentIndex(
const QModelIndex &mi)
2445 QModelIndex normalized = mi.sibling(mi.row(), modelColumn);
2446 if (!normalized.isValid())
2449 bool indexChanged = (normalized != currentIndex);
2451 currentIndex = QPersistentModelIndex(normalized);
2453 const QString newText = itemText(normalized);
2454 if (lineEdit->text() != newText) {
2455 lineEdit->setText(newText);
2456#if QT_CONFIG(completer)
2457 if (lineEdit && lineEdit->completer())
2458 lineEdit->completer()->setCompletionPrefix(newText);
2461 updateLineEditGeometry();
2468 const bool modelResetToEmpty = !normalized.isValid() && indexBeforeChange != -1;
2469 if (modelResetToEmpty)
2470 indexBeforeChange = -1;
2472 if (indexChanged || modelResetToEmpty) {
2473 QItemSelectionModel::SelectionFlags selectionMode = QItemSelectionModel::ClearAndSelect;
2474 if (q->view()->selectionBehavior() == QAbstractItemView::SelectRows)
2475 selectionMode.setFlag(QItemSelectionModel::Rows);
2476 if (
auto *model = q->view()->selectionModel())
2477 model->setCurrentIndex(currentIndex, selectionMode);
2480 emitCurrentIndexChanged(currentIndex);
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498QString QComboBox::currentText()
const
2500 Q_D(
const QComboBox);
2502 return d->lineEdit->text();
2503 if (d->currentIndex.isValid())
2504 return d->itemText(d->currentIndex);
2509
2510
2511
2512
2513
2514
2515
2516QVariant QComboBox::currentData(
int role)
const
2518 Q_D(
const QComboBox);
2519 return d->currentIndex.data(role);
2523
2524
2525QString QComboBox::itemText(
int index)
const
2527 Q_D(
const QComboBox);
2528 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2529 return d->itemText(mi);
2533
2534
2535QIcon QComboBox::itemIcon(
int index)
const
2537 Q_D(
const QComboBox);
2538 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2539 return d->itemIcon(mi);
2543
2544
2545
2546QVariant QComboBox::itemData(
int index,
int role)
const
2548 Q_D(
const QComboBox);
2549 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2550 return d->model->data(mi, role);
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579void QComboBox::insertItem(
int index,
const QIcon &icon,
const QString &text,
const QVariant &userData)
2582 int itemCount = count();
2583 index = qBound(0, index, itemCount);
2584 if (index >= d->maxCount)
2589 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
2590 QStandardItem *item =
new QStandardItem(text);
2591 if (!icon.isNull()) item->setData(icon, Qt::DecorationRole);
2592 if (userData.isValid()) item->setData(userData, Qt::UserRole);
2593 m->insertRow(index, item);
2596 d->inserting =
true;
2597 if (d->model->insertRows(index, 1, d->root)) {
2598 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2599 if (icon.isNull() && !userData.isValid()) {
2600 d->model->setData(item, text, Qt::EditRole);
2602 QMap<
int, QVariant> values;
2603 if (!text.isNull()) values.insert(Qt::EditRole, text);
2604 if (!icon.isNull()) values.insert(Qt::DecorationRole, icon);
2605 if (userData.isValid()) values.insert(Qt::UserRole, userData);
2606 if (!values.isEmpty()) d->model->setItemData(item, values);
2608 d->inserting =
false;
2609 d->rowsInserted(d->root, index, index);
2612 d->inserting =
false;
2616 if (itemCount > d->maxCount)
2617 d->model->removeRows(itemCount - 1, itemCount - d->maxCount, d->root);
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630void QComboBox::insertItems(
int index,
const QStringList &list)
2635 index = qBound(0, index, count());
2636 int insertCount = qMin(d->maxCount - index, list.size());
2637 if (insertCount <= 0)
2641 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
2642 QList<QStandardItem *> items;
2643 items.reserve(insertCount);
2644 QStandardItem *hiddenRoot = m->invisibleRootItem();
2645 for (
int i = 0; i < insertCount; ++i)
2646 items.append(
new QStandardItem(list.at(i)));
2647 hiddenRoot->insertRows(index, items);
2649 d->inserting =
true;
2650 if (d->model->insertRows(index, insertCount, d->root)) {
2652 for (
int i = 0; i < insertCount; ++i) {
2653 item = d->model->index(i+index, d->modelColumn, d->root);
2654 d->model->setData(item, list.at(i), Qt::EditRole);
2656 d->inserting =
false;
2657 d->rowsInserted(d->root, index, index + insertCount - 1);
2659 d->inserting =
false;
2664 if (mc > d->maxCount)
2665 d->model->removeRows(d->maxCount, mc - d->maxCount, d->root);
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679void QComboBox::insertSeparator(
int index)
2682 int itemCount = count();
2683 index = qBound(0, index, itemCount);
2684 if (index >= d->maxCount)
2686 insertItem(index, QIcon(), QString());
2687 QComboBoxDelegate::setSeparator(d->model, d->model->index(index, 0, d->root));
2691
2692
2693
2694
2695
2696void QComboBox::removeItem(
int index)
2699 if (index < 0 || index >= count())
2701 d->model->removeRows(index, 1, d->root);
2705
2706
2707void QComboBox::setItemText(
int index,
const QString &text)
2709 Q_D(
const QComboBox);
2710 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2711 if (item.isValid()) {
2712 d->model->setData(item, text, Qt::EditRole);
2717
2718
2719void QComboBox::setItemIcon(
int index,
const QIcon &icon)
2721 Q_D(
const QComboBox);
2722 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2723 if (item.isValid()) {
2724 d->model->setData(item, icon, Qt::DecorationRole);
2729
2730
2731
2732void QComboBox::setItemData(
int index,
const QVariant &value,
int role)
2734 Q_D(
const QComboBox);
2735 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2736 if (item.isValid()) {
2737 d->model->setData(item, value, role);
2742
2743
2744QAbstractItemView *QComboBox::view()
const
2746 Q_D(
const QComboBox);
2747 return const_cast<QComboBoxPrivate*>(d)->viewContainer()->itemView();
2751
2752
2753
2754
2755
2756
2757
2758
2759void QComboBox::setView(QAbstractItemView *itemView)
2762 if (Q_UNLIKELY(!itemView)) {
2763 qWarning(
"QComboBox::setView: cannot set a 0 view");
2767 if (itemView->model() != d->model) {
2768 d->disconnectModel();
2769 itemView->setModel(d->model);
2772 d->viewContainer()->setItemView(itemView);
2776
2777
2778QSize QComboBox::minimumSizeHint()
const
2780 Q_D(
const QComboBox);
2781 return d->recomputeSizeHint(d->minimumSizeHint);
2785
2786
2787
2788
2789
2790
2791QSize QComboBox::sizeHint()
const
2793 Q_D(
const QComboBox);
2794 return d->recomputeSizeHint(d->sizeHint);
2798void QComboBoxPrivate::cleanupNativePopup()
2800 if (!m_platformMenu)
2803 m_platformMenu->setVisible(
false);
2804 int count =
int(m_platformMenu->tag());
2805 for (
int i = 0; i < count; ++i)
2806 m_platformMenu->menuItemAt(i)->deleteLater();
2808 delete m_platformMenu;
2809 m_platformMenu =
nullptr;
2813
2814
2815
2816
2817
2818bool QComboBoxPrivate::showNativePopup()
2822 cleanupNativePopup();
2824 QPlatformTheme *theme = QGuiApplicationPrivate::instance()->platformTheme();
2825 m_platformMenu = theme->createPlatformMenu();
2826 if (!m_platformMenu)
2829 int itemsCount = q->count();
2830 m_platformMenu->setTag(quintptr(itemsCount));
2832 QPlatformMenuItem *currentItem =
nullptr;
2833 int currentIndex = q->currentIndex();
2835 for (
int i = 0; i < itemsCount; ++i) {
2836 QPlatformMenuItem *item = theme->createPlatformMenuItem();
2837 QModelIndex rowIndex = model->index(i, modelColumn, root);
2838 QVariant textVariant = model->data(rowIndex, Qt::EditRole);
2839 item->setText(textVariant.toString());
2840 QVariant iconVariant = model->data(rowIndex, Qt::DecorationRole);
2841 const Qt::ItemFlags itemFlags = model->flags(rowIndex);
2842 if (iconVariant.canConvert<QIcon>())
2843 item->setIcon(iconVariant.value<QIcon>());
2844 item->setCheckable(
true);
2845 item->setChecked(i == currentIndex);
2846 item->setEnabled(itemFlags & Qt::ItemIsEnabled);
2847 if (!currentItem || i == currentIndex)
2850 IndexSetter setter = { i, q };
2851 QObject::connect(item, &QPlatformMenuItem::activated, q, setter);
2853 m_platformMenu->insertMenuItem(item, 0);
2854 m_platformMenu->syncMenuItem(item);
2857 QWindow *tlw = q->window()->windowHandle();
2858 m_platformMenu->setFont(q->font());
2859 m_platformMenu->setMinimumWidth(q->rect().width());
2860 QPoint offset = QPoint(0, 7);
2861 if (q->testAttribute(Qt::WA_MacSmallSize))
2862 offset = QPoint(-1, 7);
2863 else if (q->testAttribute(Qt::WA_MacMiniSize))
2864 offset = QPoint(-2, 6);
2866 [[maybe_unused]] QPointer<QComboBox> guard(q);
2867 const QRect targetRect = QRect(tlw->mapFromGlobal(q->mapToGlobal(offset)), QSize());
2868 m_platformMenu->showPopup(tlw, QHighDpi::toNativePixels(targetRect, tlw), currentItem);
2874 QMouseEvent mouseReleased(QEvent::MouseButtonRelease, q->pos(), q->mapToGlobal(QPoint(0, 0)),
2875 Qt::LeftButton, Qt::MouseButtons(Qt::LeftButton), {});
2876 QCoreApplication::sendEvent(q, &mouseReleased);
2886
2887
2888
2889
2890
2891
2892
2893
2894void QComboBox::showPopup()
2900 QStyle *
const style =
this->style();
2901 QStyleOptionComboBox opt;
2902 initStyleOption(&opt);
2903 const bool usePopup = style->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this);
2908 || (qobject_cast<QComboBoxListView*>(view())
2909 && qobject_cast<QComboMenuDelegate*>(view()->itemDelegate())))
2910 && style->styleHint(QStyle::SH_ComboBox_UseNativePopup, &opt,
this)
2911 && d->showNativePopup())
2915 QComboBoxPrivateContainer* container = d->viewContainer();
2916 QRect listRect(style->subControlRect(QStyle::CC_ComboBox, &opt,
2917 QStyle::SC_ComboBoxListBoxPopup,
this));
2918 QRect screen = d->popupGeometry(mapToGlobal(listRect.topLeft()));
2920 QPoint below = mapToGlobal(listRect.bottomLeft());
2921 int belowHeight = screen.bottom() - below.y();
2922 QPoint above = mapToGlobal(listRect.topLeft());
2923 int aboveHeight = above.y() - screen.y();
2924 bool boundToScreen = !window()->testAttribute(Qt::WA_DontShowOnScreen);
2925 const auto listView = qobject_cast<QListView *>(d->viewContainer()->itemView());
2930 QStack<QModelIndex> toCheck;
2931 toCheck.push(view()->rootIndex());
2932#if QT_CONFIG(treeview)
2933 QTreeView *treeView = qobject_cast<QTreeView*>(view());
2934 if (treeView && treeView->header() && !treeView->header()->isHidden())
2935 listHeight += treeView->header()->height();
2937 while (!toCheck.isEmpty()) {
2938 QModelIndex parent = toCheck.pop();
2939 for (
int i = 0, end = d->model->rowCount(parent); i < end; ++i) {
2940 if (listView && listView->isRowHidden(i))
2942 QModelIndex idx = d->model->index(i, d->modelColumn, parent);
2945 listHeight += view()->visualRect(idx).height();
2946#if QT_CONFIG(treeview)
2947 if (d->model->hasChildren(idx) && treeView && treeView->isExpanded(idx))
2951 if (!usePopup && count >= d->maxVisibleItems) {
2958 listHeight += (count - 1) * container->spacing();
2959 listRect.setHeight(listHeight);
2964 int heightMargin = container->topMargin() + container->bottomMargin();
2967 const QMargins cm = container->contentsMargins();
2968 heightMargin += cm.top() + cm.bottom();
2971 const QMargins vm = view()->contentsMargins();
2972 heightMargin += vm.top() + vm.bottom();
2973 heightMargin +=
static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(view()))->top;
2974 heightMargin +=
static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(view()))->bottom;
2976 listRect.setHeight(listRect.height() + heightMargin);
2981 listRect.setHeight(listRect.height() + style->pixelMetric(QStyle::PM_MenuVMargin, &opt,
this) * 2);
2985 const int diff = d->computeWidthHint() - width();
2987 listRect.setWidth(listRect.width() + diff);
2991 container->layout()->activate();
2993 listRect.setSize( listRect.size().expandedTo(container->minimumSize())
2994 .boundedTo(container->maximumSize()));
2997 if (boundToScreen) {
2998 if (listRect.width() > screen.width() )
2999 listRect.setWidth(screen.width());
3000 if (mapToGlobal(listRect.bottomRight()).x() > screen.right()) {
3001 below.setX(screen.x() + screen.width() - listRect.width());
3002 above.setX(screen.x() + screen.width() - listRect.width());
3004 if (mapToGlobal(listRect.topLeft()).x() < screen.x() ) {
3005 below.setX(screen.x());
3006 above.setX(screen.x());
3012 listRect.moveLeft(above.x());
3021 view()->scrollToTop();
3022 const QRect currentItemRect = view()->visualRect(view()->currentIndex());
3023 const int offset = listRect.top() - currentItemRect.top();
3024 listRect.moveTop(above.y() + offset - listRect.top());
3029 const int height = !boundToScreen ? listRect.height() : qMin(listRect.height(), screen.height());
3030 listRect.setHeight(height);
3032 if (boundToScreen) {
3033 if (listRect.top() < screen.top())
3034 listRect.moveTop(screen.top());
3035 if (listRect.bottom() > screen.bottom())
3036 listRect.moveBottom(screen.bottom());
3038 }
else if (!boundToScreen || listRect.height() <= belowHeight) {
3039 listRect.moveTopLeft(below);
3040 }
else if (listRect.height() <= aboveHeight) {
3041 listRect.moveBottomLeft(above);
3042 }
else if (belowHeight >= aboveHeight) {
3043 listRect.setHeight(belowHeight);
3044 listRect.moveTopLeft(below);
3046 listRect.setHeight(aboveHeight);
3047 listRect.moveBottomLeft(above);
3051 QGuiApplication::inputMethod()->reset();
3054 const QScrollBar *sb = view()->horizontalScrollBar();
3055 const auto needHorizontalScrollBar = [
this, sb]{
3056 const Qt::ScrollBarPolicy policy = view()->horizontalScrollBarPolicy();
3057 return (policy == Qt::ScrollBarAsNeeded || policy == Qt::ScrollBarAlwaysOn)
3058 && sb->minimum() < sb->maximum();
3060 const bool neededHorizontalScrollBar = needHorizontalScrollBar();
3061 if (neededHorizontalScrollBar)
3062 listRect.adjust(0, 0, 0, sb->height());
3067 container->hideScrollers();
3068 container->setGeometry(listRect);
3071 const bool updatesEnabled = container->updatesEnabled();
3074#if QT_CONFIG(effects)
3075 bool scrollDown = (listRect.topLeft() == below);
3076 if (QApplication::isEffectEnabled(Qt::UI_AnimateCombo)
3077 && !style->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this) && !window()->testAttribute(Qt::WA_DontShowOnScreen))
3078 qScrollEffect(container, scrollDown ? QEffects::DownScroll : QEffects::UpScroll, 150);
3087 container->setUpdatesEnabled(
false);
3090 bool startTimer = !container->isVisible();
3092 container->create();
3093 if (QWindow *containerWindow = qt_widget_private(container)->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel)) {
3094 QScreen *currentScreen = d->associatedScreen();
3095 if (currentScreen && !currentScreen->virtualSiblings().contains(containerWindow->screen())) {
3096 containerWindow->setScreen(currentScreen);
3105#if QT_CONFIG(wayland)
3106 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(container->windowHandle()->handle())) {
3107 const QRect popup(style->subControlRect(QStyle::CC_ComboBox, &opt,
3108 QStyle::SC_ComboBoxListBoxPopup,
this));
3109 const QRect controlGeometry = QRect(mapTo(window(), popup.topLeft()), popup.size());
3110 waylandWindow->setParentControlGeometry(controlGeometry);
3111 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::ComboBox);
3116 if (!neededHorizontalScrollBar && needHorizontalScrollBar()) {
3117 listRect.adjust(0, 0, 0, sb->height());
3118 container->setGeometry(listRect);
3121 container->updateScrollers();
3124 view()->scrollTo(view()->currentIndex(),
3125 style->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this)
3126 ? QAbstractItemView::PositionAtCenter
3127 : QAbstractItemView::EnsureVisible);
3130 container->setUpdatesEnabled(updatesEnabled);
3133 container->update();
3135 container->popupTimer.start();
3136 container->maybeIgnoreMouseButtonRelease =
true;
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150void QComboBox::hidePopup()
3155 d->hidingPopup =
true;
3157 auto resetHidingPopup = qScopeGuard([d]{
3158 d->hidingPopup =
false;
3161 if (!d->container || !d->container->isVisible())
3164#if QT_CONFIG(effects)
3165 QItemSelectionModel *selectionModel = d->container->itemView()
3166 ? d->container->itemView()->selectionModel() :
nullptr;
3168 if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem,
nullptr,
this) &&
3169 selectionModel && selectionModel->hasSelection()) {
3170 const QItemSelection selection = selectionModel->selection();
3172 QTimer::singleShot(0, d->container, [d, selection, selectionModel]{
3173 QSignalBlocker modelBlocker(d->model);
3174 QSignalBlocker viewBlocker(d->container->itemView());
3175 QSignalBlocker containerBlocker(d->container);
3178 selectionModel->select(selection, QItemSelectionModel::Toggle);
3179 QTimer::singleShot(60, d->container, [d, selection, selectionModel]{
3180 QSignalBlocker modelBlocker(d->model);
3181 QSignalBlocker viewBlocker(d->container->itemView());
3182 QSignalBlocker containerBlocker(d->container);
3183 selectionModel->select(selection, QItemSelectionModel::Toggle);
3184 QTimer::singleShot(20, d->container, [d] {
3196void QComboBoxPrivate::doHidePopup()
3198 if (container && container->isVisible())
3204void QComboBoxPrivate::updateCurrentText(
const QString &text)
3206 if (text == currentText)
3210 emit q_func()->currentTextChanged(text);
3214
3215
3216
3217
3218
3219void QComboBox::clear()
3222 d->model->removeRows(0, d->model->rowCount(d->root), d->root);
3223#if QT_CONFIG(accessibility)
3224 QAccessibleValueChangeEvent event(
this, QString());
3225 QAccessible::updateAccessibility(&event);
3230
3231
3232void QComboBox::clearEditText()
3236 d->lineEdit->clear();
3237#if QT_CONFIG(accessibility)
3238 QAccessibleValueChangeEvent event(
this, QString());
3239 QAccessible::updateAccessibility(&event);
3244
3245
3246void QComboBox::setEditText(
const QString &text)
3250 d->lineEdit->setText(text);
3251#if QT_CONFIG(accessibility)
3252 QAccessibleValueChangeEvent event(
this, text);
3253 QAccessible::updateAccessibility(&event);
3258
3259
3260void QComboBox::focusInEvent(QFocusEvent *e)
3265 d->lineEdit->event(e);
3266#if QT_CONFIG(completer)
3267 if (d->lineEdit->completer())
3268 d->lineEdit->completer()->setWidget(
this);
3274
3275
3276void QComboBox::focusOutEvent(QFocusEvent *e)
3281 d->lineEdit->event(e);
3285void QComboBox::changeEvent(QEvent *e)
3288 switch (e->type()) {
3289 case QEvent::StyleChange:
3291 d->container->updateStyleSettings();
3292 d->updateDelegate();
3295 case QEvent::MacSizeChange:
3297 d->sizeHint = QSize();
3298 d->minimumSizeHint = QSize();
3299 d->updateLayoutDirection();
3301 d->updateLineEditGeometry();
3302 d->setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
3304 if (e->type() == QEvent::MacSizeChange) {
3305 QPlatformTheme::Font f = QPlatformTheme::SystemFont;
3306 if (testAttribute(Qt::WA_MacSmallSize))
3307 f = QPlatformTheme::SmallFont;
3308 else if (testAttribute(Qt::WA_MacMiniSize))
3309 f = QPlatformTheme::MiniFont;
3310 if (
const QFont *platformFont = QApplicationPrivate::platformTheme()->font(f)) {
3312 f.setPointSizeF(platformFont->pointSizeF());
3318 case QEvent::EnabledChange:
3322 case QEvent::PaletteChange: {
3323 d->updateViewContainerPaletteAndOpacity();
3326 case QEvent::FontChange: {
3327 d->sizeHint = QSize();
3328 d->viewContainer()->setFont(font());
3329 d->viewContainer()->itemView()->doItemsLayout();
3331 d->updateLineEditGeometry();
3337 QWidget::changeEvent(e);
3341
3342
3343void QComboBox::resizeEvent(QResizeEvent *)
3346 d->updateLineEditGeometry();
3350
3351
3352void QComboBox::paintEvent(QPaintEvent *)
3355 QStylePainter painter(
this);
3356 painter.setPen(palette().color(QPalette::Text));
3359 QStyleOptionComboBox opt;
3360 initStyleOption(&opt);
3361 painter.drawComplexControl(QStyle::CC_ComboBox, opt);
3363 if (currentIndex() < 0 && !placeholderText().isEmpty()) {
3364 opt.palette.setBrush(QPalette::ButtonText, opt.palette.placeholderText());
3365 opt.currentText = placeholderText();
3369 if (itemDelegate() && labelDrawingMode() == QComboBox::LabelDrawingMode::UseDelegate) {
3370 QStyleOptionViewItem itemOption;
3371 d->initViewItemOption(&itemOption);
3372 itemOption.rect = style()->subControlRect(QStyle::CC_ComboBox, &opt,
3373 QStyle::SC_ComboBoxEditField,
this);
3374 itemDelegate()->paint(&painter, itemOption, d->currentIndex);
3377 painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
3382
3383
3384void QComboBox::showEvent(QShowEvent *e)
3387 if (!d->shownOnce && d->sizeAdjustPolicy == QComboBox::AdjustToContentsOnFirstShow) {
3388 d->sizeHint = QSize();
3391 d->shownOnce =
true;
3392 QWidget::showEvent(e);
3396
3397
3398void QComboBox::hideEvent(QHideEvent *)
3404
3405
3406bool QComboBox::event(QEvent *event)
3409 switch(event->type()) {
3410 case QEvent::LayoutDirectionChange:
3411 case QEvent::ApplicationLayoutDirectionChange:
3412 d->updateLayoutDirection();
3413 d->updateLineEditGeometry();
3415 case QEvent::HoverEnter:
3416 case QEvent::HoverLeave:
3417 case QEvent::HoverMove:
3418 if (
const QHoverEvent *he =
static_cast<
const QHoverEvent *>(event))
3419 d->updateHoverControl(he->position().toPoint());
3421 case QEvent::ShortcutOverride:
3423 return d->lineEdit->event(event);
3425#ifdef QT_KEYPAD_NAVIGATION
3426 case QEvent::EnterEditFocus:
3428 d->lineEdit->event(event);
3430 case QEvent::LeaveEditFocus:
3432 d->lineEdit->event(event);
3438 return QWidget::event(event);
3442
3443
3444void QComboBox::mousePressEvent(QMouseEvent *e)
3447 if (!QGuiApplication::styleHints()->setFocusOnTouchRelease())
3448 d->showPopupFromMouseEvent(e);
3451void QComboBoxPrivate::showPopupFromMouseEvent(QMouseEvent *e)
3454 QStyleOptionComboBox opt;
3455 q->initStyleOption(&opt);
3456 QStyle::SubControl sc = q->style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt, e->position().toPoint(), q);
3458 if (e->button() == Qt::LeftButton
3459 && !(sc == QStyle::SC_None && e->type() == QEvent::MouseButtonRelease)
3460 && (sc == QStyle::SC_ComboBoxArrow || !q->isEditable())
3461 && !viewContainer()->isVisible()) {
3462 if (sc == QStyle::SC_ComboBoxArrow)
3463 updateArrow(QStyle::State_Sunken);
3464#ifdef QT_KEYPAD_NAVIGATION
3472 viewContainer()->initialClickPosition = q->mapToGlobal(e->position().toPoint());
3474 QPointer<QComboBox> guard = q;
3481 if (viewContainer()) {
3482 viewContainer()->blockMouseReleaseTimer.start(QApplication::doubleClickInterval());
3483 viewContainer()->maybeIgnoreMouseButtonRelease =
false;
3486#ifdef QT_KEYPAD_NAVIGATION
3487 if (QApplicationPrivate::keypadNavigationEnabled() && sc == QStyle::SC_ComboBoxEditField && lineEdit) {
3497
3498
3499void QComboBox::mouseReleaseEvent(QMouseEvent *e)
3502 d->updateArrow(QStyle::State_None);
3503 if (QGuiApplication::styleHints()->setFocusOnTouchRelease() && hasFocus())
3504 d->showPopupFromMouseEvent(e);
3508
3509
3510void QComboBox::keyPressEvent(QKeyEvent *e)
3514#if QT_CONFIG(completer)
3515 if (
const auto *cmpltr = completer()) {
3516 const auto *popup = QCompleterPrivate::get(cmpltr)->popup;
3517 if (popup && popup->isVisible()) {
3519 d->lineEdit->event(e);
3525 enum Move { NoMove=0 , MoveUp , MoveDown , MoveFirst , MoveLast};
3528 int newIndex = currentIndex();
3530 bool pressLikeButton = !d->lineEdit;
3531#ifdef QT_KEYPAD_NAVIGATION
3532 pressLikeButton |= QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus();
3534 auto key = e->key();
3535 if (pressLikeButton) {
3536 const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
3537 ->themeHint(QPlatformTheme::ButtonPressKeys)
3538 .value<QList<Qt::Key>>();
3539 if (buttonPressKeys.contains(key)) {
3547 if (e->modifiers() & Qt::ControlModifier)
3550 case Qt::Key_PageUp:
3551#ifdef QT_KEYPAD_NAVIGATION
3552 if (QApplicationPrivate::keypadNavigationEnabled())
3559 if (e->modifiers() & Qt::AltModifier) {
3562 }
else if (e->modifiers() & Qt::ControlModifier)
3565 case Qt::Key_PageDown:
3566#ifdef QT_KEYPAD_NAVIGATION
3567 if (QApplicationPrivate::keypadNavigationEnabled())
3582 if (!e->modifiers()) {
3588 case Qt::Key_Return:
3589 case Qt::Key_Escape:
3593#ifdef QT_KEYPAD_NAVIGATION
3596 if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus())
3600 if (QApplicationPrivate::keypadNavigationEnabled()) {
3601 if (!hasEditFocus() || !d->lineEdit)
3609#if QT_CONFIG(shortcut)
3610 if (d->container && d->container->isVisible() && e->matches(QKeySequence::Cancel)) {
3617 const auto text = e->text();
3618 if (!text.isEmpty() && text.at(0).isPrint())
3619 d->keyboardSearchString(text);
3625 const int rowCount = count();
3627 if (move != NoMove) {
3635 while (newIndex < rowCount && !(d->model->index(newIndex, d->modelColumn, d->root).flags() & Qt::ItemIsEnabled))
3639 newIndex = rowCount;
3643 while ((newIndex >= 0) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled))
3651 if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) {
3652 setCurrentIndex(newIndex);
3653 d->emitActivated(d->currentIndex);
3655 }
else if (d->lineEdit) {
3656 d->lineEdit->event(e);
3662
3663
3664void QComboBox::keyReleaseEvent(QKeyEvent *e)
3668 d->lineEdit->event(e);
3670 QWidget::keyReleaseEvent(e);
3674
3675
3676#if QT_CONFIG(wheelevent)
3677void QComboBox::wheelEvent(QWheelEvent *e)
3680 QStyleOptionComboBox opt;
3681 initStyleOption(&opt);
3682 if (style()->styleHint(QStyle::SH_ComboBox_AllowWheelScrolling, &opt,
this) &&
3683 !d->viewContainer()->isVisible()) {
3684 const int rowCount = count();
3685 int newIndex = currentIndex();
3686 int delta = e->angleDelta().y();
3690 while ((newIndex >= 0) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled))
3692 }
else if (delta < 0) {
3694 while (newIndex < rowCount && !(d->model->index(newIndex, d->modelColumn, d->root).flags() & Qt::ItemIsEnabled))
3698 if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) {
3699 setCurrentIndex(newIndex);
3700 d->emitActivated(d->currentIndex);
3709#ifndef QT_NO_CONTEXTMENU
3711
3712
3713void QComboBox::contextMenuEvent(QContextMenuEvent *e)
3717 Qt::ContextMenuPolicy p = d->lineEdit->contextMenuPolicy();
3718 d->lineEdit->setContextMenuPolicy(Qt::DefaultContextMenu);
3719 d->lineEdit->event(e);
3720 d->lineEdit->setContextMenuPolicy(p);
3725void QComboBoxPrivate::keyboardSearchString(
const QString &text)
3728 QAbstractItemView *view = viewContainer()->itemView();
3729 view->setCurrentIndex(currentIndex);
3730 int currentRow = view->currentIndex().row();
3731 view->keyboardSearch(text);
3732 if (currentRow != view->currentIndex().row()) {
3733 setCurrentIndex(view->currentIndex());
3734 emitActivated(currentIndex);
3738void QComboBoxPrivate::modelChanged()
3742 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
3744 adjustComboBoxSize();
3745 q->updateGeometry();
3750
3751
3752void QComboBox::inputMethodEvent(QInputMethodEvent *e)
3756 d->lineEdit->event(e);
3758 if (!e->commitString().isEmpty())
3759 d->keyboardSearchString(e->commitString());
3766
3767
3768QVariant QComboBox::inputMethodQuery(Qt::InputMethodQuery query)
const
3770 Q_D(
const QComboBox);
3772 return d->lineEdit->inputMethodQuery(query);
3773 return QWidget::inputMethodQuery(query);
3777
3778QVariant QComboBox::inputMethodQuery(Qt::InputMethodQuery query,
const QVariant &argument)
const
3780 Q_D(
const QComboBox);
3782 return d->lineEdit->inputMethodQuery(query, argument);
3783 return QWidget::inputMethodQuery(query);
3787
3788
3789
3790
3791
3792
3795
3796
3797
3798
3799
3800
3801
3804
3805
3806
3807
3808
3811
3812
3813
3814
3815
3818
3819
3820
3821
3822
3823
3824
3825bool QComboBox::hasFrame()
const
3827 Q_D(
const QComboBox);
3832void QComboBox::setFrame(
bool enable)
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853int QComboBox::modelColumn()
const
3855 Q_D(
const QComboBox);
3856 return d->modelColumn;
3859void QComboBox::setModelColumn(
int visibleColumn)
3862 d->modelColumn = visibleColumn;
3863 QListView *lv = qobject_cast<QListView *>(d->viewContainer()->itemView());
3865 lv->setModelColumn(visibleColumn);
3866#if QT_CONFIG(completer)
3867 if (d->lineEdit && d->lineEdit->completer())
3868 d->lineEdit->completer()->setCompletionColumn(visibleColumn);
3870 setCurrentIndex(currentIndex());
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899QComboBox::LabelDrawingMode QComboBox::labelDrawingMode()
const
3901 Q_D(
const QComboBox);
3902 return d->labelDrawingMode;
3905void QComboBox::setLabelDrawingMode(LabelDrawingMode drawingLabel)
3908 if (d->labelDrawingMode != drawingLabel) {
3909 d->labelDrawingMode = drawingLabel;
3916#include "moc_qcombobox.cpp"
3917#include "moc_qcombobox_p.cpp"
void initViewItemOption(QStyleOptionViewItem *option) const override
~QComboBoxListView() override
Combined button and popup list for selecting options.