1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30
31#include "qcombobox.h"
32#include <private/qcombobox_p.h>
33#include <private/qguiapplication_p.h>
34#include <qpa/qplatformtheme.h>
35
36#include <qfontcombobox.h>
37#include <qapplication.h>
38#include <qpushbutton.h>
39#include <qdialog.h>
40#include <qevent.h>
41#include <qlineedit.h>
42#include <qlabel.h>
43#include <qlistview.h>
44#include <qheaderview.h>
45#include <qlistwidget.h>
46#include <qtreewidget.h>
47#include <qtablewidget.h>
48#include <qscrollbar.h>
49#include <qboxlayout.h>
50#include <qstackedwidget.h>
51
52#include <qstandarditemmodel.h>
53#include <qstringlistmodel.h>
54#include <qcombobox.h>
55#include <qpushbutton.h>
56#include <qdialog.h>
57#include <qstringlist.h>
58#include <qvalidator.h>
59#include <qcompleter.h>
60#include <qstylefactory.h>
61#include <qabstractitemview.h>
62#include <qstyleditemdelegate.h>
63#include <qstandarditemmodel.h>
64#include <qproxystyle.h>
65#include <qfont.h>
66
67#include "../../../shared/platforminputcontext.h"
68#include <private/qinputmethod_p.h>
69
70#include <QtTest/private/qtesthelpers_p.h>
71
72using namespace QTestPrivate;
73
74class tst_QComboBox : public QObject
75{
76 Q_OBJECT
77
78public:
79 tst_QComboBox() {}
80
81private slots:
82 void initTestCase();
83 void cleanupTestCase();
84 void getSetCheck();
85 void ensureReturnIsIgnored();
86 void setEditable();
87 void setPalette();
88 void sizeAdjustPolicy();
89 void clear();
90 void insertPolicy_data();
91 void insertPolicy();
92 void virtualAutocompletion();
93 void autoCompletionCaseSensitivity();
94 void hide();
95 void currentIndex_data();
96 void currentIndex();
97 void insertItems_data();
98 void insertItems();
99 void insertItem_data();
100 void insertItem();
101 void insertOnCurrentIndex();
102 void textpixmapdata_data();
103 void textpixmapdata();
104 void currentTextChanged_data();
105 void currentTextChanged();
106 void editTextChanged();
107 void setModel();
108 void modelDeleted();
109 void setMaxCount();
110 void setCurrentIndex();
111 void setCurrentText_data();
112 void setCurrentText();
113 void convenienceViews();
114 void findText_data();
115 void findText();
116 void flaggedItems_data();
117 void flaggedItems();
118 void pixmapIcon();
119#if QT_CONFIG(wheelevent)
120 void mouseWheel_data();
121 void mouseWheel();
122 void popupWheelHandling();
123#endif // QT_CONFIG(wheelevent)
124 void layoutDirection();
125 void itemListPosition();
126 void separatorItem_data();
127 void separatorItem();
128#ifndef QT_NO_STYLE_FUSION
129 void task190351_layout();
130 void task191329_size();
131#endif
132 void task166349_setEditableOnReturn();
133 void task190205_setModelAdjustToContents();
134 void task248169_popupWithMinimalSize();
135 void task247863_keyBoardSelection();
136 void task220195_keyBoardSelection2();
137 void setModelColumn();
138 void noScrollbar_data();
139 void noScrollbar();
140 void setItemDelegate();
141 void task253944_itemDelegateIsReset();
142 void subControlRectsWithOffset_data();
143 void subControlRectsWithOffset();
144#ifndef QT_NO_STYLE_WINDOWS
145 void task260974_menuItemRectangleForComboBoxPopup();
146#endif
147 void removeItem();
148 void resetModel();
149 void keyBoardNavigationWithMouse();
150 void task_QTBUG_1071_changingFocusEmitsActivated();
151 void maxVisibleItems_data();
152 void maxVisibleItems();
153 void task_QTBUG_10491_currentIndexAndModelColumn();
154 void highlightedSignal();
155 void itemData();
156 void task_QTBUG_31146_popupCompletion();
157 void task_QTBUG_41288_completerChangesCurrentIndex();
158 void task_QTBUG_54191_slotOnEditTextChangedSetsComboBoxToReadOnly();
159 void keyboardSelection();
160 void setCustomModelAndView();
161 void updateDelegateOnEditableChange();
162 void respectChangedOwnershipOfItemView();
163 void task_QTBUG_39088_inputMethodHints();
164 void task_QTBUG_49831_scrollerNotActivated();
165 void task_QTBUG_56693_itemFontFromModel();
166 void inputMethodUpdate();
167 void task_QTBUG_52027_mapCompleterIndex();
168 void checkMenuItemPosWhenStyleSheetIsSet();
169 void checkEmbeddedLineEditWhenStyleSheetIsSet();
170 void propagateStyleChanges();
171
172private:
173 PlatformInputContext m_platformInputContext;
174};
175
176class MyAbstractItemDelegate : public QAbstractItemDelegate
177{
178public:
179 MyAbstractItemDelegate() : QAbstractItemDelegate() {};
180 void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const {}
181 QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const { return QSize(); }
182};
183
184class MyAbstractItemModel: public QAbstractItemModel
185{
186public:
187 MyAbstractItemModel() : QAbstractItemModel() {};
188 QModelIndex index(int, int, const QModelIndex &) const { return QModelIndex(); }
189 QModelIndex parent(const QModelIndex &) const { return QModelIndex(); }
190 int rowCount(const QModelIndex &) const { return 0; }
191 int columnCount(const QModelIndex &) const { return 0; }
192 bool hasChildren(const QModelIndex &) const { return false; }
193 QVariant data(const QModelIndex &, int) const { return QVariant(); }
194 bool setData(const QModelIndex &, const QVariant &, int) { return false; }
195 bool insertRows(int, int, const QModelIndex &) { return false; }
196 bool insertColumns(int, int, const QModelIndex &) { return false; }
197 void setPersistent(const QModelIndex &, const QModelIndex &) {}
198 bool removeRows (int, int, const QModelIndex &) { return false; }
199 bool removeColumns(int, int, const QModelIndex &) { return false; }
200 void reset() {}
201};
202
203class MyAbstractItemView : public QAbstractItemView
204{
205public:
206 MyAbstractItemView() : QAbstractItemView() {}
207 QRect visualRect(const QModelIndex &) const { return QRect(); }
208 void scrollTo(const QModelIndex &, ScrollHint) {}
209 QModelIndex indexAt(const QPoint &) const { return QModelIndex(); }
210protected:
211 QModelIndex moveCursor(CursorAction, Qt::KeyboardModifiers) { return QModelIndex(); }
212 int horizontalOffset() const { return 0; }
213 int verticalOffset() const { return 0; }
214 bool isIndexHidden(const QModelIndex &) const { return false; }
215 void setSelection(const QRect &, QItemSelectionModel::SelectionFlags) {}
216 QRegion visualRegionForSelection(const QItemSelection &) const { return QRegion(); }
217};
218
219void tst_QComboBox::initTestCase()
220{
221 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
222 inputMethodPrivate->testContext = &m_platformInputContext;
223}
224
225void tst_QComboBox::cleanupTestCase()
226{
227 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
228 inputMethodPrivate->testContext = 0;
229}
230
231// Testing get/set functions
232void tst_QComboBox::getSetCheck()
233{
234 QComboBox obj1;
235 // int QComboBox::maxVisibleItems()
236 // void QComboBox::setMaxVisibleItems(int)
237 obj1.setMaxVisibleItems(100);
238 QCOMPARE(100, obj1.maxVisibleItems());
239 obj1.setMaxVisibleItems(0);
240 QCOMPARE(obj1.maxVisibleItems(), 0);
241 QTest::ignoreMessage(type: QtWarningMsg, message: "QComboBox::setMaxVisibleItems: "
242 "Invalid max visible items (-2147483648) must be >= 0");
243 obj1.setMaxVisibleItems(INT_MIN);
244 QCOMPARE(obj1.maxVisibleItems(), 0); // Cannot be set to something negative => old value
245 obj1.setMaxVisibleItems(INT_MAX);
246 QCOMPARE(INT_MAX, obj1.maxVisibleItems());
247
248 // int QComboBox::maxCount()
249 // void QComboBox::setMaxCount(int)
250 obj1.setMaxCount(0);
251 QCOMPARE(0, obj1.maxCount());
252#ifndef QT_DEBUG
253 QTest::ignoreMessage(QtWarningMsg, "QComboBox::setMaxCount: Invalid count (-2147483648) must be >= 0");
254 obj1.setMaxCount(INT_MIN);
255 QCOMPARE(0, obj1.maxCount()); // Setting a value below 0 makes no sense, and shouldn't be allowed
256#endif
257 obj1.setMaxCount(INT_MAX);
258 QCOMPARE(INT_MAX, obj1.maxCount());
259
260 // QCompleter *QComboBox::completer()
261 // void QComboBox::setCompleter(QCompleter *)
262 obj1.setCompleter(nullptr);
263 QCOMPARE(nullptr, obj1.completer());
264 QCompleter completer;
265 obj1.setCompleter(&completer);
266 QVERIFY(obj1.completer() == nullptr); // no QLineEdit is set
267
268#if QT_DEPRECATED_SINCE(5, 13)
269 // bool QComboBox::autoCompletion()
270 // void QComboBox::setAutoCompletion(bool)
271 obj1.setAutoCompletion(false);
272 QCOMPARE(false, obj1.autoCompletion());
273 obj1.setAutoCompletion(true);
274 QCOMPARE(true, obj1.autoCompletion());
275#endif
276
277 // bool QComboBox::duplicatesEnabled()
278 // void QComboBox::setDuplicatesEnabled(bool)
279 obj1.setDuplicatesEnabled(false);
280 QCOMPARE(false, obj1.duplicatesEnabled());
281 obj1.setDuplicatesEnabled(true);
282 QCOMPARE(true, obj1.duplicatesEnabled());
283
284 // InsertPolicy QComboBox::insertPolicy()
285 // void QComboBox::setInsertPolicy(InsertPolicy)
286 obj1.setInsertPolicy(QComboBox::InsertPolicy(QComboBox::NoInsert));
287 QCOMPARE(QComboBox::InsertPolicy(QComboBox::NoInsert), obj1.insertPolicy());
288 obj1.setInsertPolicy(QComboBox::InsertPolicy(QComboBox::InsertAtTop));
289 QCOMPARE(QComboBox::InsertPolicy(QComboBox::InsertAtTop), obj1.insertPolicy());
290 obj1.setInsertPolicy(QComboBox::InsertPolicy(QComboBox::InsertAtCurrent));
291 QCOMPARE(QComboBox::InsertPolicy(QComboBox::InsertAtCurrent), obj1.insertPolicy());
292 obj1.setInsertPolicy(QComboBox::InsertPolicy(QComboBox::InsertAtBottom));
293 QCOMPARE(QComboBox::InsertPolicy(QComboBox::InsertAtBottom), obj1.insertPolicy());
294 obj1.setInsertPolicy(QComboBox::InsertPolicy(QComboBox::InsertAfterCurrent));
295 QCOMPARE(QComboBox::InsertPolicy(QComboBox::InsertAfterCurrent), obj1.insertPolicy());
296 obj1.setInsertPolicy(QComboBox::InsertPolicy(QComboBox::InsertBeforeCurrent));
297 QCOMPARE(QComboBox::InsertPolicy(QComboBox::InsertBeforeCurrent), obj1.insertPolicy());
298 obj1.setInsertPolicy(QComboBox::InsertPolicy(QComboBox::InsertAlphabetically));
299 QCOMPARE(QComboBox::InsertPolicy(QComboBox::InsertAlphabetically), obj1.insertPolicy());
300
301 // SizeAdjustPolicy QComboBox::sizeAdjustPolicy()
302 // void QComboBox::setSizeAdjustPolicy(SizeAdjustPolicy)
303 obj1.setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy(QComboBox::AdjustToContents));
304 QCOMPARE(QComboBox::SizeAdjustPolicy(QComboBox::AdjustToContents), obj1.sizeAdjustPolicy());
305 obj1.setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow));
306 QCOMPARE(QComboBox::SizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow), obj1.sizeAdjustPolicy());
307 obj1.setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength));
308 QCOMPARE(QComboBox::SizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength), obj1.sizeAdjustPolicy());
309 obj1.setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon));
310 QCOMPARE(QComboBox::SizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon), obj1.sizeAdjustPolicy());
311
312 // int QComboBox::minimumContentsLength()
313 // void QComboBox::setMinimumContentsLength(int)
314 obj1.setMinimumContentsLength(0);
315 QCOMPARE(0, obj1.minimumContentsLength());
316 obj1.setMinimumContentsLength(100);
317 QCOMPARE(100, obj1.minimumContentsLength());
318 obj1.setMinimumContentsLength(INT_MIN);
319 QCOMPARE(100, obj1.minimumContentsLength()); // Cannot be set to something negative => old value
320 obj1.setMinimumContentsLength(INT_MAX);
321 QCOMPARE(INT_MAX, obj1.minimumContentsLength());
322
323 // QLineEdit * QComboBox::lineEdit()
324 // void QComboBox::setLineEdit(QLineEdit *)
325 QLineEdit *var8 = new QLineEdit(0);
326 obj1.setLineEdit(var8);
327 QCOMPARE(var8, obj1.lineEdit());
328 QTest::ignoreMessage(type: QtWarningMsg, message: "QComboBox::setLineEdit: cannot set a 0 line edit");
329 obj1.setLineEdit((QLineEdit *)0);
330 QCOMPARE(var8, obj1.lineEdit());
331 // delete var8; // No delete, since QComboBox takes ownership
332
333 // After setting a line edit, completer() should not return nullptr anymore
334 QVERIFY(obj1.completer() != nullptr);
335
336 // const QValidator * QComboBox::validator()
337 // void QComboBox::setValidator(const QValidator *)
338 QIntValidator *var9 = new QIntValidator(0);
339 obj1.setValidator(var9);
340 QCOMPARE(obj1.validator(), (const QValidator *)var9);
341 obj1.setValidator((QValidator *)0);
342 QCOMPARE(obj1.validator(), nullptr);
343 delete var9;
344
345 // QAbstractItemDelegate * QComboBox::itemDelegate()
346 // void QComboBox::setItemDelegate(QAbstractItemDelegate *)
347 MyAbstractItemDelegate *var10 = new MyAbstractItemDelegate;
348 obj1.setItemDelegate(var10);
349 QCOMPARE(obj1.itemDelegate(), var10);
350 QTest::ignoreMessage(type: QtWarningMsg, message: "QComboBox::setItemDelegate: cannot set a 0 delegate");
351 obj1.setItemDelegate((QAbstractItemDelegate *)0);
352 QCOMPARE(obj1.itemDelegate(), var10);
353 // delete var10; // No delete, since QComboBox takes ownership
354
355 // QAbstractItemModel * QComboBox::model()
356 // void QComboBox::setModel(QAbstractItemModel *)
357 MyAbstractItemModel *var11 = new MyAbstractItemModel;
358 obj1.setModel(var11);
359 QCOMPARE(obj1.model(), (QAbstractItemModel *)var11);
360 QTest::ignoreMessage(type: QtWarningMsg, message: "QComboBox::setModel: cannot set a 0 model");
361 obj1.setModel((QAbstractItemModel *)0);
362 QCOMPARE(obj1.model(), (QAbstractItemModel *)var11);
363 delete var11;
364 obj1.model();
365
366 // int QComboBox::modelColumn()
367 // void QComboBox::setModelColumn(int)
368 obj1.setModelColumn(0);
369 QCOMPARE(0, obj1.modelColumn());
370 obj1.setModelColumn(INT_MIN);
371// QCOMPARE(0, obj1.modelColumn()); // Cannot be set to something negative => column 0
372 obj1.setModelColumn(INT_MAX);
373 QCOMPARE(INT_MAX, obj1.modelColumn());
374 obj1.setModelColumn(0); // back to normal
375
376 // QAbstractItemView * QComboBox::view()
377 // void QComboBox::setView(QAbstractItemView *)
378 MyAbstractItemView *var13 = new MyAbstractItemView;
379 obj1.setView(var13);
380 QCOMPARE(obj1.view(), (QAbstractItemView *)var13);
381 QTest::ignoreMessage(type: QtWarningMsg, message: "QComboBox::setView: cannot set a 0 view");
382 obj1.setView((QAbstractItemView *)0);
383 QCOMPARE(obj1.view(), (QAbstractItemView *)var13);
384 delete var13;
385
386 // int QComboBox::currentIndex()
387 // void QComboBox::setCurrentIndex(int)
388 obj1.setEditable(false);
389 obj1.setCurrentIndex(0);
390 QCOMPARE(-1, obj1.currentIndex());
391 obj1.setCurrentIndex(INT_MIN);
392 QCOMPARE(-1, obj1.currentIndex());
393 obj1.setCurrentIndex(INT_MAX);
394 QCOMPARE(-1, obj1.currentIndex());
395 obj1.addItems(texts: QStringList() << "1" << "2" << "3" << "4" << "5");
396 obj1.setCurrentIndex(0);
397 QCOMPARE(0, obj1.currentIndex()); // Valid
398 obj1.setCurrentIndex(INT_MIN);
399 QCOMPARE(-1, obj1.currentIndex()); // Invalid => -1
400 obj1.setCurrentIndex(4);
401 QCOMPARE(4, obj1.currentIndex()); // Valid
402 obj1.setCurrentIndex(INT_MAX);
403 QCOMPARE(-1, obj1.currentIndex()); // Invalid => -1
404
405 obj1.setIconSize(QSize(64, 32));
406 QCOMPARE(obj1.iconSize(), QSize(64, 32));
407 obj1.setIconSize(QSize());
408 const int iconWidth = obj1.style()->pixelMetric(metric: QStyle::PM_SmallIconSize, option: nullptr, widget: &obj1);
409 QCOMPARE(obj1.iconSize(), QSize(iconWidth, iconWidth));
410
411 const QString placeholderText("Please select");
412 obj1.setCurrentIndex(1);
413 obj1.setPlaceholderText(placeholderText);
414 QCOMPARE(obj1.placeholderText(), placeholderText);
415 QCOMPARE(obj1.currentText(), "2");
416 QCOMPARE(obj1.currentIndex(), 1);
417 obj1.setPlaceholderText(QString()); // should not change anything
418 QCOMPARE(obj1.placeholderText(), QString());
419 QCOMPARE(obj1.currentText(), "2");
420
421 obj1.clear();
422 obj1.setPlaceholderText(placeholderText);
423 obj1.addItems(texts: {"1", "2", "3", "4", "5"});
424 QCOMPARE(obj1.currentText(), QString());
425 QCOMPARE(obj1.currentIndex(), -1);
426 obj1.setPlaceholderText(QString()); // should not change anything
427 QCOMPARE(obj1.currentText(), "1");
428 QCOMPARE(obj1.currentIndex(), 0);
429}
430
431typedef QList<QVariant> VariantList;
432typedef QList<QIcon> IconList;
433
434Q_DECLARE_METATYPE(QComboBox::InsertPolicy)
435
436class TestWidget : public QWidget
437{
438public:
439 TestWidget() : QWidget(0, Qt::Window), m_comboBox(new QComboBox(this))
440 {
441 setObjectName("parent");
442 move(ax: 200, ay: 200);
443 resize(w: 400, h: 400);
444 m_comboBox->setGeometry(ax: 0, ay: 0, aw: 100, ah: 100);
445 m_comboBox->setObjectName("testObject");
446 m_comboBox->setEditable(false);
447 }
448
449 QComboBox *comboBox() const { return m_comboBox; }
450
451private:
452 QComboBox *m_comboBox;
453
454
455};
456
457void tst_QComboBox::setEditable()
458{
459 TestWidget topLevel;
460 topLevel.show();
461 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
462 QComboBox *testWidget = topLevel.comboBox();
463 // make sure we have no lineedit
464 QVERIFY(!testWidget->lineEdit());
465 // test setEditable(true)
466 testWidget->setEditable(true);
467 QVERIFY(testWidget->lineEdit());
468 testWidget->addItem(atext: "foo");
469 QCOMPARE(testWidget->lineEdit()->text(), QString("foo"));
470 // test setEditable(false)
471
472 QLineEdit *lineEdit = testWidget->lineEdit();
473 // line edit is visible when combobox is editable
474 QVERIFY(lineEdit->isVisible());
475 testWidget->setEditable(false);
476 QVERIFY(!testWidget->lineEdit());
477 // line edit should have been explicitly hidden when editable was turned off
478 QVERIFY(!lineEdit->isVisible());
479}
480
481
482void tst_QComboBox::setPalette()
483{
484#ifdef Q_OS_MAC
485 if (QApplication::style()->inherits("QMacStyle")) {
486 QSKIP("This test doesn't make sense for pixmap-based styles");
487 }
488#endif
489 TestWidget topLevel;
490 topLevel.show();
491 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
492 QComboBox *testWidget = topLevel.comboBox();
493 QPalette pal = testWidget->palette();
494 pal.setColor(acr: QPalette::Base, acolor: Qt::red);
495 testWidget->setPalette(pal);
496 testWidget->setEditable(!testWidget->isEditable());
497
498 pal.setColor(acr: QPalette::Base, acolor: Qt::blue);
499 testWidget->setPalette(pal);
500
501 const QObjectList comboChildren = testWidget->children();
502 for (int i = 0; i < comboChildren.size(); ++i) {
503 QObject *o = comboChildren.at(i);
504 if (o->isWidgetType()) {
505 QCOMPARE(((QWidget*)o)->palette(), pal);
506 }
507 }
508
509 testWidget->setEditable(true);
510 pal.setColor(acr: QPalette::Base, acolor: Qt::red);
511 //Setting it on the lineedit should be separate form the combo
512 testWidget->lineEdit()->setPalette(pal);
513 QVERIFY(testWidget->palette() != pal);
514 QCOMPARE(testWidget->lineEdit()->palette(), pal);
515 pal.setColor(acr: QPalette::Base, acolor: Qt::green);
516 //Setting it on the combo directly should override lineedit
517 testWidget->setPalette(pal);
518 QCOMPARE(testWidget->palette(), pal);
519 QCOMPARE(testWidget->lineEdit()->palette(), pal);
520}
521
522void tst_QComboBox::sizeAdjustPolicy()
523{
524 TestWidget topLevel;
525 topLevel.show();
526 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
527 QComboBox *testWidget = topLevel.comboBox();
528 // test that adding new items will not change the sizehint for AdjustToContentsOnFirstShow
529 QVERIFY(!testWidget->count());
530 QCOMPARE(testWidget->sizeAdjustPolicy(), QComboBox::AdjustToContentsOnFirstShow);
531 QVERIFY(testWidget->isVisible());
532 QSize firstShow = testWidget->sizeHint();
533 testWidget->addItem(atext: "normal item");
534 QCOMPARE(testWidget->sizeHint(), firstShow);
535
536 // check that with minimumContentsLength/AdjustToMinimumContentsLength sizehint changes
537 testWidget->setMinimumContentsLength(30);
538 QCOMPARE(testWidget->sizeHint(), firstShow);
539 testWidget->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
540 QSize minimumContentsLength = testWidget->sizeHint();
541 QVERIFY(minimumContentsLength.width() > firstShow.width());
542 testWidget->setMinimumContentsLength(60);
543 QVERIFY(minimumContentsLength.width() < testWidget->sizeHint().width());
544
545 // check that with minimumContentsLength/AdjustToMinimumContentsLengthWithIcon sizehint changes
546 testWidget->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
547 testWidget->setMinimumContentsLength(30);
548 minimumContentsLength = testWidget->sizeHint();
549 QVERIFY(minimumContentsLength.width() > firstShow.width());
550 testWidget->setMinimumContentsLength(60);
551 QVERIFY(minimumContentsLength.width() < testWidget->sizeHint().width());
552 minimumContentsLength = testWidget->sizeHint();
553 testWidget->setIconSize(QSize(128,128));
554 QVERIFY(minimumContentsLength.width() < testWidget->sizeHint().width());
555
556 // check AdjustToContents changes with content
557 testWidget->setSizeAdjustPolicy(QComboBox::AdjustToContents);
558 QSize content = testWidget->sizeHint();
559 testWidget->addItem(atext: "small");
560 QCOMPARE(testWidget->sizeHint(), content);
561 testWidget->addItem(atext: "looooooooooooooooooooooong item");
562 // minimumContentsLength() > sizeof("looooooooooooooooooooooong item"), so the sizeHint()
563 // stays the same
564 QCOMPARE(testWidget->sizeHint(), content);
565 // over 60 characters (cf. setMinimumContentsLength() call above)
566 testWidget->addItem(atext: "loooooooooooooooooooooooooooooooooooooooooooooo"
567 "ooooooooooooooooooooooooooooooooooooooooooooooo"
568 "ooooooooooooooooooooooooooooong item");
569 QVERIFY(testWidget->sizeHint().width() > content.width());
570
571 // check AdjustToContents also shrinks when item changes
572 content = testWidget->sizeHint();
573 for (int i=0; i<testWidget->count(); ++i)
574 testWidget->setItemText(index: i, text: "XXXXXXXXXX");
575 QVERIFY(testWidget->sizeHint().width() < content.width());
576
577 // check AdjustToContents shrinks when items are removed
578 content = testWidget->sizeHint();
579 while (testWidget->count())
580 testWidget->removeItem(index: 0);
581 QCOMPARE(testWidget->sizeHint(), content);
582 testWidget->setMinimumContentsLength(0);
583 QVERIFY(testWidget->sizeHint().width() < content.width());
584
585 // check AdjustToContents changes when model changes
586 content = testWidget->sizeHint();
587 QStandardItemModel *model = new QStandardItemModel(2, 1, testWidget);
588 testWidget->setModel(model);
589 QVERIFY(testWidget->sizeHint().width() < content.width());
590
591 // check AdjustToContents changes when a row is inserted into the model
592 content = testWidget->sizeHint();
593 QStandardItem *item = new QStandardItem(QStringLiteral("This is an item"));
594 model->appendRow(aitem: item);
595 QVERIFY(testWidget->sizeHint().width() > content.width());
596
597 // check AdjustToContents changes when model is reset
598 content = testWidget->sizeHint();
599 model->clear();
600 QVERIFY(testWidget->sizeHint().width() < content.width());
601}
602
603void tst_QComboBox::clear()
604{
605 TestWidget topLevel;
606 topLevel.show();
607 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
608 QComboBox *testWidget = topLevel.comboBox();
609 // first non editable combobox
610 testWidget->addItem(atext: "foo");
611 testWidget->addItem(atext: "bar");
612 QVERIFY(testWidget->count() > 0);
613 QCOMPARE(testWidget->currentIndex(), 0);
614
615 testWidget->clear();
616 QCOMPARE(testWidget->count(), 0);
617 QCOMPARE(testWidget->currentIndex(), -1);
618 QVERIFY(testWidget->currentText().isEmpty());
619
620 // then editable combobox
621 testWidget->clear();
622 testWidget->setEditable(true);
623 testWidget->addItem(atext: "foo");
624 testWidget->addItem(atext: "bar");
625 QVERIFY(testWidget->count() > 0);
626 QCOMPARE(testWidget->currentIndex(), 0);
627 QVERIFY(testWidget->lineEdit());
628 QVERIFY(!testWidget->lineEdit()->text().isEmpty());
629 testWidget->clear();
630 QCOMPARE(testWidget->count(), 0);
631 QCOMPARE(testWidget->currentIndex(), -1);
632 QVERIFY(testWidget->currentText().isEmpty());
633 QVERIFY(testWidget->lineEdit()->text().isEmpty());
634}
635
636void tst_QComboBox::insertPolicy_data()
637{
638 QTest::addColumn<QStringList>(name: "initialEntries");
639 QTest::addColumn<QComboBox::InsertPolicy>(name: "insertPolicy");
640 QTest::addColumn<int>(name: "currentIndex");
641 QTest::addColumn<QString>(name: "userInput");
642 QTest::addColumn<QStringList>(name: "result");
643
644 /* Each insertPolicy should test at least:
645 no initial entries
646 one initial entry
647 five initial entries, current is first item
648 five initial entries, current is third item
649 five initial entries, current is last item
650 */
651
652 /* QComboBox::NoInsert - the string will not be inserted into the combobox.
653 QComboBox::InsertAtTop - insert the string as the first item in the combobox.
654 QComboBox::InsertAtCurrent - replace the previously selected item with the string the user has entered.
655 QComboBox::InsertAtBottom - insert the string as the last item in the combobox.
656 QComboBox::InsertAfterCurrent - insert the string after the previously selected item.
657 QComboBox::InsertBeforeCurrent - insert the string before the previously selected item.
658 QComboBox::InsertAlphabetically - insert the string at the alphabetic position.
659 */
660 QStringList initial;
661 QStringList oneEntry("One");
662 QStringList fiveEntries;
663 fiveEntries << "One" << "Two" << "Three" << "Four" << "Five";
664 QString input("insert");
665
666 {
667 QTest::newRow(dataTag: "NoInsert-NoInitial") << initial << QComboBox::NoInsert << 0 << input << initial;
668 QTest::newRow(dataTag: "NoInsert-OneInitial") << oneEntry << QComboBox::NoInsert << 0 << input << oneEntry;
669 QTest::newRow(dataTag: "NoInsert-FiveInitial-FirstCurrent") << fiveEntries << QComboBox::NoInsert << 0 << input << fiveEntries;
670 QTest::newRow(dataTag: "NoInsert-FiveInitial-ThirdCurrent") << fiveEntries << QComboBox::NoInsert << 2 << input << fiveEntries;
671 QTest::newRow(dataTag: "NoInsert-FiveInitial-LastCurrent") << fiveEntries << QComboBox::NoInsert << 4 << input << fiveEntries;
672 }
673
674 {
675 QStringList initialAtTop("insert");
676 QStringList oneAtTop;
677 oneAtTop << "insert" << "One";
678 QStringList fiveAtTop;
679 fiveAtTop << "insert" << "One" << "Two" << "Three" << "Four" << "Five";
680
681 QTest::newRow(dataTag: "AtTop-NoInitial") << initial << QComboBox::InsertAtTop << 0 << input << initialAtTop;
682 QTest::newRow(dataTag: "AtTop-OneInitial") << oneEntry << QComboBox::InsertAtTop << 0 << input << oneAtTop;
683 QTest::newRow(dataTag: "AtTop-FiveInitial-FirstCurrent") << fiveEntries << QComboBox::InsertAtTop << 0 << input << fiveAtTop;
684 QTest::newRow(dataTag: "AtTop-FiveInitial-ThirdCurrent") << fiveEntries << QComboBox::InsertAtTop << 2 << input << fiveAtTop;
685 QTest::newRow(dataTag: "AtTop-FiveInitial-LastCurrent") << fiveEntries << QComboBox::InsertAtTop << 4 << input << fiveAtTop;
686 }
687
688 {
689 QStringList initialAtCurrent("insert");
690 QStringList oneAtCurrent("insert");
691 QStringList fiveAtCurrentFirst;
692 fiveAtCurrentFirst << "insert" << "Two" << "Three" << "Four" << "Five";
693 QStringList fiveAtCurrentThird;
694 fiveAtCurrentThird << "One" << "Two" << "insert" << "Four" << "Five";
695 QStringList fiveAtCurrentLast;
696 fiveAtCurrentLast << "One" << "Two" << "Three" << "Four" << "insert";
697
698 QTest::newRow(dataTag: "AtCurrent-NoInitial") << initial << QComboBox::InsertAtCurrent << 0 << input << initialAtCurrent;
699 QTest::newRow(dataTag: "AtCurrent-OneInitial") << oneEntry << QComboBox::InsertAtCurrent << 0 << input << oneAtCurrent;
700 QTest::newRow(dataTag: "AtCurrent-FiveInitial-FirstCurrent") << fiveEntries << QComboBox::InsertAtCurrent << 0 << input << fiveAtCurrentFirst;
701 QTest::newRow(dataTag: "AtCurrent-FiveInitial-ThirdCurrent") << fiveEntries << QComboBox::InsertAtCurrent << 2 << input << fiveAtCurrentThird;
702 QTest::newRow(dataTag: "AtCurrent-FiveInitial-LastCurrent") << fiveEntries << QComboBox::InsertAtCurrent << 4 << input << fiveAtCurrentLast;
703 }
704
705 {
706 QStringList initialAtBottom("insert");
707 QStringList oneAtBottom;
708 oneAtBottom << "One" << "insert";
709 QStringList fiveAtBottom;
710 fiveAtBottom << "One" << "Two" << "Three" << "Four" << "Five" << "insert";
711
712 QTest::newRow(dataTag: "AtBottom-NoInitial") << initial << QComboBox::InsertAtBottom << 0 << input << initialAtBottom;
713 QTest::newRow(dataTag: "AtBottom-OneInitial") << oneEntry << QComboBox::InsertAtBottom << 0 << input << oneAtBottom;
714 QTest::newRow(dataTag: "AtBottom-FiveInitial-FirstCurrent") << fiveEntries << QComboBox::InsertAtBottom << 0 << input << fiveAtBottom;
715 QTest::newRow(dataTag: "AtBottom-FiveInitial-ThirdCurrent") << fiveEntries << QComboBox::InsertAtBottom << 2 << input << fiveAtBottom;
716 QTest::newRow(dataTag: "AtBottom-FiveInitial-LastCurrent") << fiveEntries << QComboBox::InsertAtBottom << 4 << input << fiveAtBottom;
717 }
718
719 {
720 QStringList initialAfterCurrent("insert");
721 QStringList oneAfterCurrent;
722 oneAfterCurrent << "One" << "insert";
723 QStringList fiveAfterCurrentFirst;
724 fiveAfterCurrentFirst << "One" << "insert" << "Two" << "Three" << "Four" << "Five";
725 QStringList fiveAfterCurrentThird;
726 fiveAfterCurrentThird << "One" << "Two" << "Three" << "insert" << "Four" << "Five";
727 QStringList fiveAfterCurrentLast;
728 fiveAfterCurrentLast << "One" << "Two" << "Three" << "Four" << "Five" << "insert";
729
730 QTest::newRow(dataTag: "AfterCurrent-NoInitial") << initial << QComboBox::InsertAfterCurrent << 0 << input << initialAfterCurrent;
731 QTest::newRow(dataTag: "AfterCurrent-OneInitial") << oneEntry << QComboBox::InsertAfterCurrent << 0 << input << oneAfterCurrent;
732 QTest::newRow(dataTag: "AfterCurrent-FiveInitial-FirstCurrent") << fiveEntries << QComboBox::InsertAfterCurrent << 0 << input << fiveAfterCurrentFirst;
733 QTest::newRow(dataTag: "AfterCurrent-FiveInitial-ThirdCurrent") << fiveEntries << QComboBox::InsertAfterCurrent << 2 << input << fiveAfterCurrentThird;
734 QTest::newRow(dataTag: "AfterCurrent-FiveInitial-LastCurrent") << fiveEntries << QComboBox::InsertAfterCurrent << 4 << input << fiveAfterCurrentLast;
735 }
736
737 {
738 QStringList initialBeforeCurrent("insert");
739 QStringList oneBeforeCurrent;
740 oneBeforeCurrent << "insert" << "One";
741 QStringList fiveBeforeCurrentFirst;
742 fiveBeforeCurrentFirst << "insert" << "One" << "Two" << "Three" << "Four" << "Five";
743 QStringList fiveBeforeCurrentThird;
744 fiveBeforeCurrentThird << "One" << "Two" << "insert" << "Three" << "Four" << "Five";
745 QStringList fiveBeforeCurrentLast;
746 fiveBeforeCurrentLast << "One" << "Two" << "Three" << "Four" << "insert" << "Five";
747
748 QTest::newRow(dataTag: "BeforeCurrent-NoInitial") << initial << QComboBox::InsertBeforeCurrent << 0 << input << initialBeforeCurrent;
749 QTest::newRow(dataTag: "BeforeCurrent-OneInitial") << oneEntry << QComboBox::InsertBeforeCurrent << 0 << input << oneBeforeCurrent;
750 QTest::newRow(dataTag: "BeforeCurrent-FiveInitial-FirstCurrent") << fiveEntries << QComboBox::InsertBeforeCurrent << 0 << input << fiveBeforeCurrentFirst;
751 QTest::newRow(dataTag: "BeforeCurrent-FiveInitial-ThirdCurrent") << fiveEntries << QComboBox::InsertBeforeCurrent << 2 << input << fiveBeforeCurrentThird;
752 QTest::newRow(dataTag: "BeforeCurrent-FiveInitial-LastCurrent") << fiveEntries << QComboBox::InsertBeforeCurrent << 4 << input << fiveBeforeCurrentLast;
753 }
754
755 {
756 oneEntry.clear();
757 oneEntry << "foobar";
758 fiveEntries.clear();
759 fiveEntries << "bar" << "foo" << "initial" << "Item" << "stamp";
760
761 QStringList initialAlphabetically("insert");
762 QStringList oneAlphabetically;
763 oneAlphabetically << "foobar" << "insert";
764 QStringList fiveAlphabetically;
765 fiveAlphabetically << "bar" << "foo" << "initial" << "insert" << "Item" << "stamp";
766
767 QTest::newRow(dataTag: "Alphabetically-NoInitial") << initial << QComboBox::InsertAlphabetically << 0 << input << initialAlphabetically;
768 QTest::newRow(dataTag: "Alphabetically-OneInitial") << oneEntry << QComboBox::InsertAlphabetically << 0 << input << oneAlphabetically;
769 QTest::newRow(dataTag: "Alphabetically-FiveInitial-FirstCurrent") << fiveEntries << QComboBox::InsertAlphabetically << 0 << input << fiveAlphabetically;
770 QTest::newRow(dataTag: "Alphabetically-FiveInitial-ThirdCurrent") << fiveEntries << QComboBox::InsertAlphabetically << 2 << input << fiveAlphabetically;
771 QTest::newRow(dataTag: "Alphabetically-FiveInitial-LastCurrent") << fiveEntries << QComboBox::InsertAlphabetically << 4 << input << fiveAlphabetically;
772 }
773}
774
775void tst_QComboBox::insertPolicy()
776{
777 QFETCH(QStringList, initialEntries);
778 QFETCH(QComboBox::InsertPolicy, insertPolicy);
779 QFETCH(int, currentIndex);
780 QFETCH(QString, userInput);
781 QFETCH(QStringList, result);
782
783 TestWidget topLevel;
784 topLevel.show();
785 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
786 QComboBox *testWidget = topLevel.comboBox();
787 testWidget->clear();
788 testWidget->setInsertPolicy(insertPolicy);
789 testWidget->addItems(texts: initialEntries);
790 testWidget->setEditable(true);
791 if (initialEntries.count() > 0)
792 testWidget->setCurrentIndex(currentIndex);
793
794 // clear
795 QTest::mouseDClick(widget: testWidget->lineEdit(), button: Qt::LeftButton);
796 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_Delete);
797
798 QTest::keyClicks(widget: testWidget->lineEdit(), sequence: userInput);
799 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_Return);
800
801 // First check that there is the right number of entries, or
802 // we may unwittingly pass
803 QCOMPARE((int)result.count(), testWidget->count());
804
805 // No need to compare if there are no strings to compare
806 if (result.count() > 0) {
807 for (int i=0; i<testWidget->count(); ++i) {
808 QCOMPARE(testWidget->itemText(i), result.at(i));
809 }
810 }
811}
812
813// Apps running with valgrind are not fast enough.
814void tst_QComboBox::virtualAutocompletion()
815{
816 TestWidget topLevel;
817 topLevel.show();
818 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
819 QComboBox *testWidget = topLevel.comboBox();
820 testWidget->clear();
821#if QT_DEPRECATED_SINCE(5, 13)
822 testWidget->setAutoCompletion(true);
823#endif
824 testWidget->addItem(atext: "Foo");
825 testWidget->addItem(atext: "Bar");
826 testWidget->addItem(atext: "Boat");
827 testWidget->addItem(atext: "Boost");
828 testWidget->clearEditText();
829
830 // We need to set the keyboard input interval to a higher value
831 // as the processEvent() call takes too much time, so it restarts
832 // the keyboard search then
833#if defined(Q_PROCESSOR_ARM) || defined(Q_PROCESSOR_MIPS)
834 int oldInterval = QApplication::keyboardInputInterval();
835 QApplication::setKeyboardInputInterval(1500);
836#endif
837
838 // NOTE:
839 // Cannot use keyClick for this test, as it simulates keyclicks too well
840 // The virtual keyboards we're trying to catch here, do not perform that
841 // well, and send a keypress & keyrelease right after each other.
842 // This provokes the actual error, as there's no events in between to do
843 // the text completion.
844 QKeyEvent kp1(QEvent::KeyPress, Qt::Key_B, {}, "b");
845 QKeyEvent kr1(QEvent::KeyRelease, Qt::Key_B, {}, "b");
846 QApplication::sendEvent(receiver: testWidget, event: &kp1);
847 QApplication::sendEvent(receiver: testWidget, event: &kr1);
848
849 qApp->processEvents(); // Process events to trigger autocompletion
850 QTRY_COMPARE(testWidget->currentIndex(), 1);
851
852 QKeyEvent kp2(QEvent::KeyPress, Qt::Key_O, {}, "o");
853 QKeyEvent kr2(QEvent::KeyRelease, Qt::Key_O, {}, "o");
854
855 QApplication::sendEvent(receiver: testWidget, event: &kp2);
856 QApplication::sendEvent(receiver: testWidget, event: &kr2);
857
858 qApp->processEvents(); // Process events to trigger autocompletion
859 QTRY_COMPARE(testWidget->currentIndex(), 2);
860
861 QApplication::sendEvent(receiver: testWidget, event: &kp2);
862 QApplication::sendEvent(receiver: testWidget, event: &kr2);
863 qApp->processEvents(); // Process events to trigger autocompletion
864 QTRY_COMPARE(testWidget->currentIndex(), 3);
865#if defined(Q_PROCESSOR_ARM) || defined(Q_PROCESSOR_MIPS)
866 QApplication::setKeyboardInputInterval(oldInterval);
867#endif
868}
869
870void tst_QComboBox::autoCompletionCaseSensitivity()
871{
872 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
873 QSKIP("Wayland: This fails. Figure out why.");
874
875 //we have put the focus because the completer
876 //is only used when the widget actually has the focus
877 TestWidget topLevel;
878 topLevel.show();
879 QComboBox *testWidget = topLevel.comboBox();
880 qApp->setActiveWindow(&topLevel);
881 testWidget->setFocus();
882 QVERIFY(QTest::qWaitForWindowActive(&topLevel));
883 QCOMPARE(qApp->focusWidget(), (QWidget *)testWidget);
884
885 testWidget->clear();
886#if QT_DEPRECATED_SINCE(5, 13)
887 testWidget->setAutoCompletion(true);
888#endif
889 testWidget->addItem(atext: "Cow");
890 testWidget->addItem(atext: "irrelevant1");
891 testWidget->addItem(atext: "aww");
892 testWidget->addItem(atext: "A*");
893 testWidget->addItem(atext: "irrelevant2");
894 testWidget->addItem(atext: "aBCDEF");
895 testWidget->addItem(atext: "irrelevant3");
896 testWidget->addItem(atext: "abcdef");
897 testWidget->addItem(atext: "abCdef");
898 testWidget->setEditable(true);
899
900 // case insensitive
901 testWidget->clearEditText();
902 QSignalSpy spyReturn(testWidget, SIGNAL(activated(int)));
903 testWidget->completer()->setCaseSensitivity(Qt::CaseInsensitive);
904 QCOMPARE(testWidget->completer()->caseSensitivity(), Qt::CaseInsensitive);
905
906 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_A);
907 qApp->processEvents();
908 QCOMPARE(testWidget->currentText(), QString("aww"));
909 QCOMPARE(spyReturn.count(), 0);
910
911 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_B);
912 qApp->processEvents();
913 // autocompletions preserve userkey-case from 4.2
914 QCOMPARE(testWidget->currentText(), QString("abCDEF"));
915 QCOMPARE(spyReturn.count(), 0);
916
917 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_Enter);
918 qApp->processEvents();
919 QCOMPARE(testWidget->currentText(), QString("aBCDEF")); // case restored to item's case
920 QCOMPARE(spyReturn.count(), 1);
921
922 testWidget->clearEditText();
923 QTest::keyClick(widget: testWidget->lineEdit(), key: 'c');
924 QCOMPARE(testWidget->currentText(), QString("cow"));
925 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_Enter);
926 QCOMPARE(testWidget->currentText(), QString("Cow")); // case restored to item's case
927
928 testWidget->clearEditText();
929 QTest::keyClick(widget: testWidget->lineEdit(), key: 'a');
930 QTest::keyClick(widget: testWidget->lineEdit(), key: '*');
931 QCOMPARE(testWidget->currentText(), QString("a*"));
932 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_Enter);
933 QCOMPARE(testWidget->currentText(), QString("A*"));
934
935 // case sensitive
936 testWidget->clearEditText();
937 testWidget->completer()->setCaseSensitivity(Qt::CaseSensitive);
938 QCOMPARE(testWidget->completer()->caseSensitivity(), Qt::CaseSensitive);
939 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_A);
940 qApp->processEvents();
941 QCOMPARE(testWidget->currentText(), QString("aww"));
942 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_B);
943 qApp->processEvents();
944 QCOMPARE(testWidget->currentText(), QString("abcdef"));
945
946 testWidget->setCurrentIndex(0); // to reset the completion's "start"
947 testWidget->clearEditText();
948 QTest::keyClick(widget: testWidget->lineEdit(), key: 'a');
949 QTest::keyClick(widget: testWidget->lineEdit(), key: 'b');
950 QCOMPARE(testWidget->currentText(), QString("abcdef"));
951 QTest::keyClick(widget: testWidget->lineEdit(), key: 'C');
952 qApp->processEvents();
953 QCOMPARE(testWidget->currentText(), QString("abCdef"));
954 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_Enter);
955 qApp->processEvents();
956 QCOMPARE(testWidget->currentText(), QString("abCdef")); // case restored to item's case
957
958 testWidget->clearEditText();
959 QTest::keyClick(widget: testWidget->lineEdit(), key: 'c');
960 QCOMPARE(testWidget->currentText(), QString("c"));
961 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_Backspace);
962 QTest::keyClick(widget: testWidget->lineEdit(), key: 'C');
963 QCOMPARE(testWidget->currentText(), QString("Cow"));
964 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_Enter);
965 QCOMPARE(testWidget->currentText(), QString("Cow"));
966
967 testWidget->clearEditText();
968 QTest::keyClick(widget: testWidget->lineEdit(), key: 'a');
969 QTest::keyClick(widget: testWidget->lineEdit(), key: '*');
970 QCOMPARE(testWidget->currentText(), QString("a*"));
971 QTest::keyClick(widget: testWidget->lineEdit(), key: Qt::Key_Enter);
972 QCOMPARE(testWidget->currentText(), QString("a*")); // A* not matched
973}
974
975void tst_QComboBox::hide()
976{
977 TestWidget topLevel;
978 topLevel.show();
979 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
980 QComboBox *testWidget = topLevel.comboBox();
981 testWidget->addItem(atext: "foo");
982 testWidget->showPopup();
983 //allow combobox effect to complete
984 QTRY_VERIFY(testWidget->view());
985 QTRY_VERIFY(testWidget->view()->isVisible());
986 testWidget->hidePopup();
987 //allow combobox effect to complete
988 QTRY_VERIFY(!testWidget->view()->isVisible());
989 testWidget->hide();
990 QVERIFY(!testWidget->isVisible());
991}
992
993
994
995void tst_QComboBox::currentIndex_data()
996{
997 QTest::addColumn<QStringList>(name: "initialItems");
998 QTest::addColumn<int>(name: "setCurrentIndex");
999 QTest::addColumn<int>(name: "removeIndex");
1000 QTest::addColumn<int>(name: "insertIndex");
1001 QTest::addColumn<QString>(name: "insertText");
1002 QTest::addColumn<int>(name: "expectedCurrentIndex");
1003 QTest::addColumn<QString>(name: "expectedCurrentText");
1004 QTest::addColumn<int>(name: "expectedSignalCount");
1005
1006 QStringList initialItems;
1007 int setCurrentIndex;
1008 int removeIndex;
1009 int insertIndex;
1010 QString insertText;
1011 int expectedCurrentIndex;
1012 QString expectedCurrentText;
1013 int expectedSignalCount;
1014
1015 {
1016 initialItems.clear();
1017 initialItems << "foo" << "bar";
1018 setCurrentIndex = -2;
1019 removeIndex = -1;
1020 insertIndex = -1;
1021 insertText = "";
1022 expectedCurrentIndex = 0;
1023 expectedCurrentText = "foo";
1024 expectedSignalCount = 1;
1025 QTest::newRow(dataTag: "first added item is set to current if there is no current")
1026 << initialItems << setCurrentIndex << removeIndex
1027 << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText
1028 << expectedSignalCount;
1029 }
1030 {
1031 initialItems.clear();
1032 initialItems << "foo" << "bar";
1033 setCurrentIndex = 1;
1034 removeIndex = -1;
1035 insertIndex = -1;
1036 insertText = "";
1037 expectedCurrentIndex = 1;
1038 expectedCurrentText = "bar";
1039 expectedSignalCount = 2;
1040 QTest::newRow(dataTag: "check that setting the index works")
1041 << initialItems << setCurrentIndex << removeIndex
1042 << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText
1043 << expectedSignalCount;
1044
1045 }
1046 {
1047 initialItems.clear();
1048 initialItems << "foo" << "bar";
1049 setCurrentIndex = -1; // will invalidate the currentIndex
1050 removeIndex = -1;
1051 insertIndex = -1;
1052 insertText = "";
1053 expectedCurrentIndex = -1;
1054 expectedCurrentText = "";
1055 expectedSignalCount = 2;
1056 QTest::newRow(dataTag: "check that isetting the index to -1 works")
1057 << initialItems << setCurrentIndex << removeIndex
1058 << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText
1059 << expectedSignalCount;
1060
1061 }
1062 {
1063 initialItems.clear();
1064 initialItems << "foo";
1065 setCurrentIndex = 0;
1066 removeIndex = 0;
1067 insertIndex = -1;
1068 insertText = "";
1069 expectedCurrentIndex = -1;
1070 expectedCurrentText = "";
1071 expectedSignalCount = 2;
1072 QTest::newRow(dataTag: "check that current index is invalid when removing the only item")
1073 << initialItems << setCurrentIndex << removeIndex
1074 << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText
1075 << expectedSignalCount;
1076 }
1077 {
1078 initialItems.clear();
1079 initialItems << "foo" << "bar";
1080 setCurrentIndex = 1;
1081 removeIndex = 0;
1082 insertIndex = -1;
1083 insertText = "";
1084 expectedCurrentIndex = 0;
1085 expectedCurrentText = "bar";
1086 expectedSignalCount = 3;
1087 QTest::newRow(dataTag: "check that the current index follows the item when removing an item above")
1088 << initialItems << setCurrentIndex << removeIndex
1089 << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText
1090 << expectedSignalCount;
1091
1092 }
1093 {
1094 initialItems.clear();
1095 initialItems << "foo" << "bar" << "baz";
1096 setCurrentIndex = 1;
1097 removeIndex = 1;
1098 insertIndex = -1;
1099 insertText = "";
1100 expectedCurrentIndex = 1;
1101 expectedCurrentText = "baz";
1102 expectedSignalCount = 3;
1103 QTest::newRow(dataTag: "check that the current index uses the next item if current is removed")
1104 << initialItems << setCurrentIndex << removeIndex
1105 << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText
1106 << expectedSignalCount;
1107 }
1108 {
1109 initialItems.clear();
1110 initialItems << "foo" << "bar" << "baz";
1111 setCurrentIndex = 2;
1112 removeIndex = 2;
1113 insertIndex = -1;
1114 insertText = "";
1115 expectedCurrentIndex = 1;
1116 expectedCurrentText = "bar";
1117 expectedSignalCount = 3;
1118 QTest::newRow(dataTag: "check that the current index is moved to the one before if current is removed")
1119 << initialItems << setCurrentIndex << removeIndex
1120 << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText
1121 << expectedSignalCount;
1122 }
1123 {
1124 initialItems.clear();
1125 initialItems << "foo" << "bar" << "baz";
1126 setCurrentIndex = 1;
1127 removeIndex = 2;
1128 insertIndex = -1;
1129 insertText = "";
1130 expectedCurrentIndex = 1;
1131 expectedCurrentText = "bar";
1132 expectedSignalCount = 2;
1133 QTest::newRow(dataTag: "check that the current index is unchanged if you remove an item after")
1134 << initialItems << setCurrentIndex << removeIndex
1135 << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText
1136 << expectedSignalCount;
1137 }
1138 {
1139 initialItems.clear();
1140 initialItems << "foo" << "bar";
1141 setCurrentIndex = 1;
1142 removeIndex = -1;
1143 insertIndex = 0;
1144 insertText = "baz";
1145 expectedCurrentIndex = 2;
1146 expectedCurrentText = "bar";
1147 expectedSignalCount = 3;
1148 QTest::newRow(dataTag: "check that the current index follows the item if you insert before current")
1149 << initialItems << setCurrentIndex << removeIndex
1150 << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText
1151 << expectedSignalCount;
1152 }
1153 {
1154 initialItems.clear();
1155 initialItems << "foo";
1156 setCurrentIndex = 0;
1157 removeIndex = -1;
1158 insertIndex = 0;
1159 insertText = "bar";
1160 expectedCurrentIndex = 1;
1161 expectedCurrentText = "foo";
1162 expectedSignalCount = 2;
1163 QTest::newRow(dataTag: "check that the current index follows the item if you insert on the current")
1164 << initialItems << setCurrentIndex << removeIndex
1165 << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText
1166 << expectedSignalCount;
1167 }
1168 {
1169 initialItems.clear();
1170 initialItems << "foo";
1171 setCurrentIndex = 0;
1172 removeIndex = -1;
1173 insertIndex = 1;
1174 insertText = "bar";
1175 expectedCurrentIndex = 0;
1176 expectedCurrentText = "foo";
1177 expectedSignalCount = 1;
1178 QTest::newRow(dataTag: "check that the current index stays the same if you insert after the current")
1179 << initialItems << setCurrentIndex << removeIndex
1180 << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText
1181 << expectedSignalCount;
1182 }
1183}
1184
1185void tst_QComboBox::currentIndex()
1186{
1187 QFETCH(QStringList, initialItems);
1188 QFETCH(int, setCurrentIndex);
1189 QFETCH(int, removeIndex);
1190 QFETCH(int, insertIndex);
1191 QFETCH(QString, insertText);
1192 QFETCH(int, expectedCurrentIndex);
1193 QFETCH(QString, expectedCurrentText);
1194 QFETCH(int, expectedSignalCount);
1195
1196 TestWidget topLevel;
1197 topLevel.show();
1198 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1199 QComboBox *testWidget = topLevel.comboBox();
1200 // test both editable/non-editable combobox
1201 for (int edit = 0; edit < 2; ++edit) {
1202 testWidget->clear();
1203 testWidget->setEditable(edit ? true : false);
1204 if (edit)
1205 QVERIFY(testWidget->lineEdit());
1206
1207 // verify it is empty, has no current index and no current text
1208 QCOMPARE(testWidget->count(), 0);
1209 QCOMPARE(testWidget->currentIndex(), -1);
1210 QVERIFY(testWidget->currentText().isEmpty());
1211
1212 // spy on currentIndexChanged
1213 QSignalSpy indexChangedInt(testWidget, SIGNAL(currentIndexChanged(int)));
1214 QSignalSpy indexChangedString(testWidget, SIGNAL(currentIndexChanged(QString)));
1215
1216 // stuff items into it
1217 foreach(QString text, initialItems) {
1218 testWidget->addItem(atext: text);
1219 }
1220 QCOMPARE(testWidget->count(), initialItems.count());
1221
1222 // set current index, remove and/or insert
1223 if (setCurrentIndex >= -1) {
1224 testWidget->setCurrentIndex(setCurrentIndex);
1225 QCOMPARE(testWidget->currentIndex(), setCurrentIndex);
1226 }
1227
1228 if (removeIndex >= 0)
1229 testWidget->removeItem(index: removeIndex);
1230 if (insertIndex >= 0)
1231 testWidget->insertItem(aindex: insertIndex, atext: insertText);
1232
1233 // compare with expected index and text
1234 QCOMPARE(testWidget->currentIndex(), expectedCurrentIndex);
1235 QCOMPARE(testWidget->currentText(), expectedCurrentText);
1236
1237 // check that signal count is correct
1238 QCOMPARE(indexChangedInt.count(), expectedSignalCount);
1239 QCOMPARE(indexChangedString.count(), expectedSignalCount);
1240
1241 // compare with last sent signal values
1242 if (indexChangedInt.count())
1243 QCOMPARE(indexChangedInt.at(indexChangedInt.count() - 1).at(0).toInt(),
1244 testWidget->currentIndex());
1245 if (indexChangedString.count())
1246 QCOMPARE(indexChangedString.at(indexChangedString.count() - 1).at(0).toString(),
1247 testWidget->currentText());
1248
1249 if (edit) {
1250 testWidget->setCurrentIndex(-1);
1251 testWidget->setInsertPolicy(QComboBox::InsertAtBottom);
1252 QTest::keyPress(widget: testWidget, key: 'a');
1253 QTest::keyPress(widget: testWidget, key: 'b');
1254 QCOMPARE(testWidget->currentText(), QString("ab"));
1255 QCOMPARE(testWidget->currentIndex(), -1);
1256 int numItems = testWidget->count();
1257 QTest::keyPress(widget: testWidget, key: Qt::Key_Return);
1258 QCOMPARE(testWidget->count(), numItems + 1);
1259 QCOMPARE(testWidget->currentIndex(), numItems);
1260 testWidget->setCurrentIndex(-1);
1261 QTest::keyPress(widget: testWidget, key: 'a');
1262 QTest::keyPress(widget: testWidget, key: 'b');
1263 QCOMPARE(testWidget->currentIndex(), -1);
1264 }
1265 }
1266}
1267
1268void tst_QComboBox::insertItems_data()
1269{
1270 QTest::addColumn<QStringList>(name: "initialItems");
1271 QTest::addColumn<QStringList>(name: "insertedItems");
1272 QTest::addColumn<int>(name: "insertIndex");
1273 QTest::addColumn<int>(name: "expectedIndex");
1274
1275 QStringList initialItems;
1276 QStringList insertedItems;
1277
1278 initialItems << "foo" << "bar";
1279 insertedItems << "mongo";
1280
1281 QTest::newRow(dataTag: "prepend") << initialItems << insertedItems << 0 << 0;
1282 QTest::newRow(dataTag: "prepend with negative value") << initialItems << insertedItems << -1 << 0;
1283 QTest::newRow(dataTag: "append") << initialItems << insertedItems << initialItems.count() << initialItems.count();
1284 QTest::newRow(dataTag: "append with too high value") << initialItems << insertedItems << 999 << initialItems.count();
1285 QTest::newRow(dataTag: "insert") << initialItems << insertedItems << 1 << 1;
1286}
1287
1288void tst_QComboBox::insertItems()
1289{
1290 QFETCH(QStringList, initialItems);
1291 QFETCH(QStringList, insertedItems);
1292 QFETCH(int, insertIndex);
1293 QFETCH(int, expectedIndex);
1294
1295 TestWidget topLevel;
1296 topLevel.show();
1297 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1298 QComboBox *testWidget = topLevel.comboBox();
1299 testWidget->insertItems(index: 0, texts: initialItems);
1300 QCOMPARE(testWidget->count(), initialItems.count());
1301
1302 testWidget->insertItems(index: insertIndex, texts: insertedItems);
1303
1304 QCOMPARE(testWidget->count(), initialItems.count() + insertedItems.count());
1305 for (int i=0; i<insertedItems.count(); ++i)
1306 QCOMPARE(testWidget->itemText(expectedIndex + i), insertedItems.at(i));
1307}
1308
1309void tst_QComboBox::insertItem_data()
1310{
1311 QTest::addColumn<QStringList>(name: "initialItems");
1312 QTest::addColumn<int>(name: "insertIndex");
1313 QTest::addColumn<QString>(name: "itemLabel");
1314 QTest::addColumn<int>(name: "expectedIndex");
1315 QTest::addColumn<bool>(name: "editable");
1316
1317 QStringList initialItems;
1318 initialItems << "foo" << "bar";
1319 for(int e = 0 ; e<2 ; e++) {
1320 bool editable = (e==0);
1321 QTest::newRow(dataTag: "Insert less then 0") << initialItems << -1 << "inserted" << 0 << editable;
1322 QTest::newRow(dataTag: "Insert at 0") << initialItems << 0 << "inserted" << 0 << editable;
1323 QTest::newRow(dataTag: "Insert beyond count") << initialItems << 3 << "inserted" << 2 << editable;
1324 QTest::newRow(dataTag: "Insert at count") << initialItems << 2 << "inserted" << 2 << editable;
1325 QTest::newRow(dataTag: "Insert in the middle") << initialItems << 1 << "inserted" << 1 << editable;
1326 }
1327}
1328
1329void tst_QComboBox::insertItem()
1330{
1331 QFETCH(QStringList, initialItems);
1332 QFETCH(int, insertIndex);
1333 QFETCH(QString, itemLabel);
1334 QFETCH(int, expectedIndex);
1335 QFETCH(bool, editable);
1336
1337 TestWidget topLevel;
1338 topLevel.show();
1339 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1340 QComboBox *testWidget = topLevel.comboBox();
1341 testWidget->insertItems(index: 0, texts: initialItems);
1342 QCOMPARE(testWidget->count(), initialItems.count());
1343
1344 testWidget->setEditable(true);
1345 if (editable)
1346 testWidget->setEditText("FOO");
1347 testWidget->insertItem(aindex: insertIndex, atext: itemLabel);
1348
1349 QCOMPARE(testWidget->count(), initialItems.count() + 1);
1350 QCOMPARE(testWidget->itemText(expectedIndex), itemLabel);
1351
1352 if (editable)
1353 QCOMPARE(testWidget->currentText(), QString("FOO"));
1354}
1355
1356void tst_QComboBox::insertOnCurrentIndex()
1357{
1358 TestWidget topLevel;
1359 topLevel.show();
1360 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1361 QComboBox *testWidget = topLevel.comboBox();
1362 testWidget->setEditable(true);
1363 testWidget->addItem(atext: "first item");
1364 testWidget->setCurrentIndex(0);
1365 testWidget->insertItem(aindex: 0, atext: "second item");
1366 QCOMPARE(testWidget->lineEdit()->text(), QString::fromLatin1("first item"));
1367}
1368
1369void tst_QComboBox::textpixmapdata_data()
1370{
1371 QTest::addColumn<QStringList>(name: "text");
1372 QTest::addColumn<IconList>(name: "icons");
1373 QTest::addColumn<VariantList>(name: "variant");
1374
1375 QStringList text;
1376 IconList icon;
1377 VariantList variant;
1378 QString qtlogoPath = QFINDTESTDATA("qtlogo.png");
1379 QString qtlogoinvertedPath = QFINDTESTDATA("qtlogoinverted.png");
1380
1381 {
1382 text.clear(); icon.clear(); variant.clear();
1383 text << "foo" << "bar";
1384 icon << QIcon() << QIcon();
1385 variant << QVariant() << QVariant();
1386 QTest::newRow(dataTag: "just text") << text << icon << variant;
1387 }
1388 {
1389 text.clear(); icon.clear(); variant.clear();
1390 text << QString() << QString();
1391 icon << QIcon(QPixmap(qtlogoPath)) << QIcon(QPixmap(qtlogoinvertedPath));
1392 variant << QVariant() << QVariant();
1393 QTest::newRow(dataTag: "just icons") << text << icon << variant;
1394 }
1395 {
1396 text.clear(); icon.clear(); variant.clear();
1397 text << QString() << QString();
1398 icon << QIcon() << QIcon();
1399 variant << 12 << "bingo";
1400 QTest::newRow(dataTag: "just user data") << text << icon << variant;
1401 }
1402 {
1403 text.clear(); icon.clear(); variant.clear();
1404 text << "foo" << "bar";
1405 icon << QIcon(QPixmap(qtlogoPath)) << QIcon(QPixmap(qtlogoinvertedPath));
1406 variant << 12 << "bingo";
1407 QTest::newRow(dataTag: "text, icons and user data") << text << icon << variant;
1408 }
1409}
1410
1411void tst_QComboBox::textpixmapdata()
1412{
1413 QFETCH(QStringList, text);
1414 QFETCH(IconList, icons);
1415 QFETCH(VariantList, variant);
1416
1417 QVERIFY(text.count() == icons.count() && text.count() == variant.count());
1418
1419 TestWidget topLevel;
1420 topLevel.show();
1421 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1422 QComboBox *testWidget = topLevel.comboBox();
1423 for (int i = 0; i<text.count(); ++i) {
1424 testWidget->insertItem(aindex: i, atext: text.at(i));
1425 testWidget->setItemIcon(index: i, icon: icons.at(i));
1426 testWidget->setItemData(index: i, value: variant.at(i), role: Qt::UserRole);
1427 }
1428
1429 QCOMPARE(testWidget->count(), text.count());
1430
1431 for (int i = 0; i<text.count(); ++i) {
1432 QIcon icon = testWidget->itemIcon(index: i);
1433 QCOMPARE(icon.cacheKey(), icons.at(i).cacheKey());
1434 QPixmap original = icons.at(i).pixmap(extent: 1024);
1435 QPixmap pixmap = icon.pixmap(extent: 1024);
1436 QCOMPARE(pixmap.toImage(), original.toImage());
1437 }
1438
1439 for (int i = 0; i<text.count(); ++i) {
1440 QCOMPARE(testWidget->itemText(i), text.at(i));
1441 // ### we should test icons/pixmap as well, but I need to fix the api mismatch first
1442 QCOMPARE(testWidget->itemData(i, Qt::UserRole), variant.at(i));
1443 }
1444}
1445
1446void tst_QComboBox::setCurrentIndex()
1447{
1448 TestWidget topLevel;
1449 topLevel.show();
1450 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1451 QComboBox *testWidget = topLevel.comboBox();
1452 QCOMPARE(testWidget->count(), 0);
1453 testWidget->addItem(atext: "foo");
1454 testWidget->addItem(atext: "bar");
1455 QCOMPARE(testWidget->count(), 2);
1456
1457 QCOMPARE(testWidget->currentIndex(), 0);
1458 testWidget->setCurrentIndex(0);
1459 QCOMPARE(testWidget->currentText(), QString("foo"));
1460
1461 testWidget->setCurrentIndex(1);
1462 QCOMPARE(testWidget->currentText(), QString("bar"));
1463
1464 testWidget->setCurrentIndex(0);
1465 QCOMPARE(testWidget->currentText(), QString("foo"));
1466}
1467
1468void tst_QComboBox::setCurrentText_data()
1469{
1470 QTest::addColumn<bool>(name: "editable");
1471 QTest::newRow(dataTag: "editable") << true;
1472 QTest::newRow(dataTag: "not editable") << false;
1473}
1474
1475void tst_QComboBox::setCurrentText()
1476{
1477 QFETCH(bool, editable);
1478
1479 TestWidget topLevel;
1480 topLevel.show();
1481 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1482 QComboBox *testWidget = topLevel.comboBox();
1483 QCOMPARE(testWidget->count(), 0);
1484 testWidget->addItems(texts: QStringList() << "foo" << "bar");
1485 QCOMPARE(testWidget->count(), 2);
1486
1487 testWidget->setEditable(editable);
1488 testWidget->setCurrentIndex(0);
1489 QCOMPARE(testWidget->currentIndex(), 0);
1490
1491 // effect on currentText and currentIndex
1492 // currentIndex not changed if editable
1493 QCOMPARE(testWidget->currentText(), QString("foo"));
1494 testWidget->setCurrentText(QString("bar"));
1495 QCOMPARE(testWidget->currentText(), QString("bar"));
1496 if (editable)
1497 QCOMPARE(testWidget->currentIndex(), 0);
1498 else
1499 QCOMPARE(testWidget->currentIndex(), 1);
1500
1501 testWidget->setCurrentText(QString("foo"));
1502 QCOMPARE(testWidget->currentIndex(), 0);
1503 QCOMPARE(testWidget->currentText(), QString("foo"));
1504
1505 // effect of text not found in list
1506 testWidget->setCurrentText(QString("qt"));
1507 QCOMPARE(testWidget->currentIndex(), 0);
1508 if (editable)
1509 QCOMPARE(testWidget->currentText(), QString("qt"));
1510 else
1511 QCOMPARE(testWidget->currentText(), QString("foo"));
1512
1513#ifndef QT_NO_PROPERTIES
1514 // verify WRITE for currentText property
1515 testWidget->setCurrentIndex(0);
1516 const QByteArray n("currentText");
1517 QCOMPARE(testWidget->property(n).toString(), QString("foo"));
1518 testWidget->setProperty(name: n, value: QString("bar"));
1519 QCOMPARE(testWidget->property(n).toString(), QString("bar"));
1520#endif
1521}
1522
1523void tst_QComboBox::currentTextChanged_data()
1524{
1525 QTest::addColumn<bool>(name: "editable");
1526 QTest::newRow(dataTag: "editable") << true;
1527 QTest::newRow(dataTag: "not editable") << false;
1528}
1529
1530void tst_QComboBox::currentTextChanged()
1531{
1532 QFETCH(bool, editable);
1533
1534 TestWidget topLevel;
1535 topLevel.show();
1536 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1537 QComboBox *testWidget = topLevel.comboBox();
1538 QCOMPARE(testWidget->count(), 0);
1539 testWidget->addItems(texts: QStringList() << "foo" << "bar");
1540 QCOMPARE(testWidget->count(), 2);
1541
1542 QSignalSpy spy(testWidget, SIGNAL(currentTextChanged(QString)));
1543
1544 testWidget->setEditable(editable);
1545
1546 // set text in list
1547 testWidget->setCurrentIndex(0);
1548 QCOMPARE(testWidget->currentIndex(), 0);
1549 spy.clear();
1550 testWidget->setCurrentText(QString("bar"));
1551 QCOMPARE(spy.count(), 1);
1552 QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("bar"));
1553
1554 // set text not in list
1555 testWidget->setCurrentIndex(0);
1556 QCOMPARE(testWidget->currentIndex(), 0);
1557 spy.clear();
1558 testWidget->setCurrentText(QString("qt"));
1559 if (editable) {
1560 QCOMPARE(spy.count(), 1);
1561 QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("qt"));
1562 } else {
1563 QCOMPARE(spy.count(), 0);
1564 }
1565
1566 // item changed
1567 testWidget->setCurrentIndex(0);
1568 QCOMPARE(testWidget->currentIndex(), 0);
1569 spy.clear();
1570 testWidget->setItemText(index: 0, text: QString("ape"));
1571 QCOMPARE(spy.count(), 1);
1572 QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("ape"));
1573 // change it back
1574 spy.clear();
1575 testWidget->setItemText(index: 0, text: QString("foo"));
1576 QCOMPARE(spy.count(), 1);
1577 QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("foo"));
1578}
1579
1580void tst_QComboBox::editTextChanged()
1581{
1582 TestWidget topLevel;
1583 topLevel.show();
1584 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1585 QComboBox *testWidget = topLevel.comboBox();
1586 QCOMPARE(testWidget->count(), 0);
1587 testWidget->addItem(atext: "foo");
1588 testWidget->addItem(atext: "bar");
1589 QCOMPARE(testWidget->count(), 2);
1590
1591 // first we test non editable
1592 testWidget->setEditable(false);
1593 QCOMPARE(testWidget->isEditable(), false);
1594
1595 QSignalSpy spy(testWidget, SIGNAL(editTextChanged(QString)));
1596
1597 // no signal should be sent when current is set to the same
1598 QCOMPARE(testWidget->currentIndex(), 0);
1599 testWidget->setCurrentIndex(0);
1600 QCOMPARE(testWidget->currentIndex(), 0);
1601 QCOMPARE(spy.count(), 0);
1602
1603 // no signal should be sent when changing to other index because we are not editable
1604 QCOMPARE(testWidget->currentIndex(), 0);
1605 testWidget->setCurrentIndex(1);
1606 QCOMPARE(testWidget->currentIndex(), 1);
1607 QCOMPARE(spy.count(), 0);
1608
1609 // now set to editable and reset current index
1610 testWidget->setEditable(true);
1611 QCOMPARE(testWidget->isEditable(), true);
1612 testWidget->setCurrentIndex(0);
1613
1614 // no signal should be sent when current is set to the same
1615 spy.clear();
1616 QCOMPARE(testWidget->currentIndex(), 0);
1617 testWidget->setCurrentIndex(0);
1618 QCOMPARE(testWidget->currentIndex(), 0);
1619 QCOMPARE(spy.count(), 0);
1620
1621 // signal should be sent when changing to other index
1622 QCOMPARE(testWidget->currentIndex(), 0);
1623 testWidget->setCurrentIndex(1);
1624 QCOMPARE(testWidget->currentIndex(), 1);
1625 QCOMPARE(spy.count(), 1);
1626 QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("bar"));
1627
1628
1629 // insert some keys and notice they are all signaled
1630 spy.clear();
1631 QTest::keyClicks(widget: testWidget, sequence: "bingo");
1632 QCOMPARE(spy.count(), 5);
1633 QCOMPARE(qvariant_cast<QString>(spy.at(4).at(0)), QString("barbingo"));
1634}
1635
1636void tst_QComboBox::setModel()
1637{
1638 QComboBox box;
1639 QCOMPARE(box.currentIndex(), -1);
1640 box.addItems(texts: (QStringList() << "foo" << "bar"));
1641 QCOMPARE(box.currentIndex(), 0);
1642 box.setCurrentIndex(1);
1643 QCOMPARE(box.currentIndex(), 1);
1644
1645 // check that currentIndex is set to invalid
1646 QAbstractItemModel *oldModel = box.model();
1647 box.setModel(new QStandardItemModel(&box));
1648 QCOMPARE(box.currentIndex(), -1);
1649 QVERIFY(box.model() != oldModel);
1650
1651 // check that currentIndex is set to first item
1652 oldModel = box.model();
1653 box.setModel(new QStandardItemModel(2,1, &box));
1654 QCOMPARE(box.currentIndex(), 0);
1655 QVERIFY(box.model() != oldModel);
1656
1657 // set a new root index
1658 QModelIndex rootModelIndex;
1659 rootModelIndex = box.model()->index(row: 0, column: 0);
1660 QVERIFY(rootModelIndex.isValid());
1661 box.setRootModelIndex(rootModelIndex);
1662 QCOMPARE(box.rootModelIndex(), rootModelIndex);
1663
1664 // change the model, ensure that the root index gets reset
1665 oldModel = box.model();
1666 box.setModel(new QStandardItemModel(2, 1, &box));
1667 QCOMPARE(box.currentIndex(), 0);
1668 QVERIFY(box.model() != oldModel);
1669 QVERIFY(box.rootModelIndex() != rootModelIndex);
1670 QCOMPARE(box.rootModelIndex(), QModelIndex());
1671
1672 // check that setting the very same model doesn't move the current item
1673 box.setCurrentIndex(1);
1674 QCOMPARE(box.currentIndex(), 1);
1675 box.setModel(box.model());
1676 QCOMPARE(box.currentIndex(), 1);
1677
1678 // check that setting the very same model doesn't move the root index
1679 rootModelIndex = box.model()->index(row: 0, column: 0);
1680 QVERIFY(rootModelIndex.isValid());
1681 box.setRootModelIndex(rootModelIndex);
1682 QCOMPARE(box.rootModelIndex(), rootModelIndex);
1683 box.setModel(box.model());
1684 QCOMPARE(box.rootModelIndex(), rootModelIndex);
1685
1686 // check that setting the same model as the completer's doesn't crash
1687 QCompleter *completer = new QCompleter(&box);
1688 box.setEditable(true);
1689 box.setCompleter(completer);
1690 auto *listModel = new QStringListModel({ "one", "two" }, completer);
1691 completer->setModel(listModel);
1692 QCOMPARE(listModel->rowCount(), 2); // make sure it wasn't deleted
1693 box.setModel(listModel);
1694 QCOMPARE(listModel->rowCount(), 2); // make sure it wasn't deleted
1695}
1696
1697void tst_QComboBox::setCustomModelAndView()
1698{
1699 // QTBUG-27597, ensure the correct text is returned when using custom view and a tree model.
1700 QComboBox combo;
1701 combo.setWindowTitle("QTBUG-27597, setCustomModelAndView");
1702 combo.setEditable(true);
1703 combo.setMinimumWidth(400);
1704 const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
1705 combo.move(availableGeometry.center() - QPoint(200, 20));
1706
1707 QStandardItemModel *model = new QStandardItemModel(0, 1, &combo);
1708
1709 QStandardItem *item = new QStandardItem(QStringLiteral("Item1"));
1710 item->appendRow(aitem: new QStandardItem(QStringLiteral("Item11")));
1711 model->appendRow(aitem: item);
1712
1713 item = new QStandardItem(QStringLiteral("Item2"));
1714 model->appendRow(aitem: item);
1715 const QString subItem21Text = QStringLiteral("Item21");
1716 QStandardItem *subItem = new QStandardItem(subItem21Text);
1717 item->appendRow(aitem: subItem);
1718
1719 QTreeView* view = new QTreeView(&combo);
1720 view->setHeaderHidden(true);
1721 view->setSelectionMode(QAbstractItemView::SingleSelection);
1722 view->setModel(model);
1723 view->expandAll();
1724 combo.setModel(model);
1725 combo.setView(view);
1726 combo.show();
1727 QVERIFY(QTest::qWaitForWindowExposed(&combo));
1728 combo.showPopup();
1729 QTRY_VERIFY(combo.view()->isVisible());
1730 const QRect subItemRect = view->visualRect(index: model->indexFromItem(item: subItem));
1731 QWidget *window = view->window();
1732
1733 // QComboBox sometimes ignores the mouse click event for doubleClickInterval
1734 // depending on which tests have been run previously. On arm this happens
1735 // more often than on x86. Search for maybeIgnoreMouseButtonRelease to see
1736 // why this happens.
1737 QTest::qWait(ms: QApplication::doubleClickInterval());
1738
1739 QTest::mouseClick(window: window->windowHandle(), button: Qt::LeftButton, stateKey: {}, pos: view->mapTo(window, subItemRect.center()));
1740#ifdef Q_OS_WINRT
1741 QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort);
1742#endif
1743 QTRY_COMPARE(combo.currentText(), subItem21Text);
1744}
1745
1746void tst_QComboBox::modelDeleted()
1747{
1748 QComboBox box;
1749 QStandardItemModel *model = new QStandardItemModel;
1750 box.setModel(model);
1751 QCOMPARE(box.model(), static_cast<QAbstractItemModel *>(model));
1752 delete model;
1753 QVERIFY(box.model());
1754 QCOMPARE(box.findText("bubu"), -1);
1755
1756 delete box.model();
1757 QVERIFY(box.model());
1758 delete box.model();
1759 QVERIFY(box.model());
1760}
1761
1762void tst_QComboBox::setMaxCount()
1763{
1764 QStringList items;
1765 items << "1" << "2" << "3" << "4" << "5";
1766
1767 QComboBox box;
1768 box.addItems(texts: items);
1769 QCOMPARE(box.count(), 5);
1770
1771 box.setMaxCount(4);
1772 QCOMPARE(box.count(), 4);
1773 QCOMPARE(box.itemText(0), QString("1"));
1774 QCOMPARE(box.itemText(1), QString("2"));
1775 QCOMPARE(box.itemText(2), QString("3"));
1776 QCOMPARE(box.itemText(3), QString("4"));
1777
1778 // appending should do nothing
1779 box.addItem(atext: "foo");
1780 QCOMPARE(box.count(), 4);
1781 QCOMPARE(box.findText("foo"), -1);
1782
1783 // inserting one item at top should remove the last
1784 box.insertItem(aindex: 0, atext: "0");
1785 QCOMPARE(box.count(), 4);
1786 QCOMPARE(box.itemText(0), QString("0"));
1787 QCOMPARE(box.itemText(1), QString("1"));
1788 QCOMPARE(box.itemText(2), QString("2"));
1789 QCOMPARE(box.itemText(3), QString("3"));
1790
1791 // insert 5 items in a box with maxCount 4
1792 box.insertItems(index: 0, texts: items);
1793 QCOMPARE(box.count(), 4);
1794 QCOMPARE(box.itemText(0), QString("1"));
1795 QCOMPARE(box.itemText(1), QString("2"));
1796 QCOMPARE(box.itemText(2), QString("3"));
1797 QCOMPARE(box.itemText(3), QString("4"));
1798
1799 // insert 5 items at pos 2. Make sure only two get inserted
1800 QSignalSpy spy(box.model(), SIGNAL(rowsInserted(QModelIndex,int,int)));
1801 box.insertItems(index: 2, texts: items);
1802 QCOMPARE(spy.count(), 1);
1803 QCOMPARE(spy.at(0).at(1).toInt(), 2);
1804 QCOMPARE(spy.at(0).at(2).toInt(), 3);
1805
1806 QCOMPARE(box.count(), 4);
1807 QCOMPARE(box.itemText(0), QString("1"));
1808 QCOMPARE(box.itemText(1), QString("2"));
1809 QCOMPARE(box.itemText(2), QString("1"));
1810 QCOMPARE(box.itemText(3), QString("2"));
1811
1812 box.insertItems(index: 0, texts: QStringList());
1813 QCOMPARE(box.count(), 4);
1814
1815 box.setMaxCount(0);
1816 QCOMPARE(box.count(), 0);
1817 box.addItem(atext: "foo");
1818 QCOMPARE(box.count(), 0);
1819 box.addItems(texts: items);
1820 QCOMPARE(box.count(), 0);
1821}
1822
1823void tst_QComboBox::convenienceViews()
1824{
1825 // QListWidget
1826 QComboBox listCombo;
1827 QListWidget *list = new QListWidget();
1828 listCombo.setModel(list->model());
1829 listCombo.setView(list);
1830 // add items
1831 list->addItem(label: "list0");
1832 listCombo.addItem(atext: "list1");
1833 QCOMPARE(listCombo.count(), 2);
1834 QCOMPARE(listCombo.itemText(0), QString("list0"));
1835 QCOMPARE(listCombo.itemText(1), QString("list1"));
1836
1837 // QTreeWidget
1838 QComboBox treeCombo;
1839 QTreeWidget *tree = new QTreeWidget();
1840 tree->setColumnCount(1);
1841 tree->header()->hide();
1842 treeCombo.setModel(tree->model());
1843 treeCombo.setView(tree);
1844 // add items
1845 tree->addTopLevelItem(item: new QTreeWidgetItem(QStringList("tree0")));
1846 treeCombo.addItem(atext: "tree1");
1847 QCOMPARE(treeCombo.count(), 2);
1848 QCOMPARE(treeCombo.itemText(0), QString("tree0"));
1849 QCOMPARE(treeCombo.itemText(1), QString("tree1"));
1850
1851 // QTableWidget
1852 QComboBox tableCombo;
1853 QTableWidget *table = new QTableWidget(0,1);
1854 table->verticalHeader()->hide();
1855 table->horizontalHeader()->hide();
1856 tableCombo.setModel(table->model());
1857 tableCombo.setView(table);
1858 // add items
1859 table->setRowCount(table->rowCount() + 1);
1860 table->setItem(row: 0, column: table->rowCount() - 1, item: new QTableWidgetItem("table0"));
1861 tableCombo.addItem(atext: "table1");
1862 QCOMPARE(tableCombo.count(), 2);
1863 QCOMPARE(tableCombo.itemText(0), QString("table0"));
1864 QCOMPARE(tableCombo.itemText(1), QString("table1"));
1865}
1866
1867class ReturnClass : public QWidget
1868{
1869 Q_OBJECT
1870public:
1871 ReturnClass(QWidget *parent = 0)
1872 : QWidget(parent), received(false)
1873 {
1874 QComboBox *box = new QComboBox(this);
1875 box->setEditable(true);
1876 edit = box->lineEdit();
1877 box->setGeometry(rect());
1878 }
1879
1880 void keyPressEvent(QKeyEvent *e)
1881 {
1882 received = (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter);
1883 }
1884
1885 QLineEdit *edit;
1886
1887 bool received;
1888
1889};
1890
1891
1892
1893void tst_QComboBox::ensureReturnIsIgnored()
1894{
1895 ReturnClass r;
1896 r.move(ax: 200, ay: 200);
1897 r.show();
1898
1899 QTest::keyClick(widget: r.edit, key: Qt::Key_Return);
1900 QVERIFY(r.received);
1901 r.received = false;
1902 QTest::keyClick(widget: r.edit, key: Qt::Key_Enter);
1903 QVERIFY(r.received);
1904}
1905
1906
1907void tst_QComboBox::findText_data()
1908{
1909 QTest::addColumn<QStringList>(name: "items");
1910 QTest::addColumn<int>(name: "matchflags");
1911 QTest::addColumn<QString>(name: "search");
1912 QTest::addColumn<int>(name: "result");
1913
1914 QStringList list;
1915 list << "One" << "Two" << "Three" << "Four" << "Five" << "Six" << "one";
1916 QTest::newRow(dataTag: "CaseSensitive_1") << list << (int)(Qt::MatchExactly|Qt::MatchCaseSensitive)
1917 << QString("Two") << 1;
1918 QTest::newRow(dataTag: "CaseSensitive_2") << list << (int)(Qt::MatchExactly|Qt::MatchCaseSensitive)
1919 << QString("two") << -1;
1920 QTest::newRow(dataTag: "CaseSensitive_3") << list << (int)(Qt::MatchExactly|Qt::MatchCaseSensitive)
1921 << QString("One") << 0;
1922 QTest::newRow(dataTag: "CaseSensitive_4") << list << (int)(Qt::MatchExactly|Qt::MatchCaseSensitive)
1923 << QString("one") << 6;
1924 QTest::newRow(dataTag: "CaseInsensitive_1") << list << (int)(Qt::MatchExactly) << QString("Two") << 1;
1925 QTest::newRow(dataTag: "CaseInsensitive_2") << list << (int)(Qt::MatchExactly) << QString("two") << -1;
1926 QTest::newRow(dataTag: "CaseInsensitive_3") << list << (int)(Qt::MatchExactly) << QString("One") << 0;
1927 QTest::newRow(dataTag: "CaseInsensitive_4") << list << (int)(Qt::MatchExactly) << QString("one") << 6;
1928}
1929void tst_QComboBox::findText()
1930{
1931 QFETCH(QStringList, items);
1932 QFETCH(int, matchflags);
1933 QFETCH(QString, search);
1934 QFETCH(int, result);
1935
1936 TestWidget topLevel;
1937 topLevel.show();
1938 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1939 QComboBox *testWidget = topLevel.comboBox();
1940 testWidget->clear();
1941 testWidget->addItems(texts: items);
1942
1943 QCOMPARE(testWidget->findText(search, (Qt::MatchFlags)matchflags), result);
1944}
1945
1946typedef QList<int> IntList;
1947typedef QList<Qt::Key> KeyList;
1948Q_DECLARE_METATYPE(KeyList)
1949
1950void tst_QComboBox::flaggedItems_data()
1951{
1952 QTest::addColumn<QStringList>(name: "itemList");
1953 QTest::addColumn<IntList>(name: "deselectFlagList");
1954 QTest::addColumn<IntList>(name: "disableFlagList");
1955 QTest::addColumn<KeyList>(name: "keyMovementList");
1956 QTest::addColumn<bool>(name: "editable");
1957 QTest::addColumn<int>(name: "expectedIndex");
1958
1959 for (int editable=0;editable<2;editable++) {
1960 QString testCase = editable ? "editable:" : "non-editable:";
1961 QStringList itemList;
1962 itemList << "One" << "Two" << "Three" << "Four" << "Five" << "Six" << "Seven" << "Eight";
1963 IntList deselectFlagList;
1964 IntList disableFlagList;
1965 KeyList keyMovementList;
1966
1967 keyMovementList << Qt::Key_Down << Qt::Key_Down << Qt::Key_Down << Qt::Key_Down;
1968 QTest::newRow(dataTag: testCase.toLatin1() + "normal") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 4;
1969
1970 deselectFlagList.clear();
1971 disableFlagList.clear();
1972 deselectFlagList << 1 << 3;
1973 QTest::newRow(dataTag: testCase.toLatin1() + "non-selectable") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 4;
1974
1975 deselectFlagList.clear();
1976 disableFlagList.clear();
1977 disableFlagList << 2;
1978 QTest::newRow(dataTag: testCase.toLatin1() + "disabled") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 5;
1979
1980 deselectFlagList.clear();
1981 disableFlagList.clear();
1982 deselectFlagList << 1 << 3;
1983 disableFlagList << 2 << 3;
1984 QTest::newRow(dataTag: testCase.toLatin1() + "mixed") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 6;
1985 deselectFlagList.clear();
1986 disableFlagList.clear();
1987 disableFlagList << 0 << 1 << 2 << 3 << 4 << 5 << 6;
1988 QTest::newRow(dataTag: testCase.toLatin1() + "nearly-empty") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 7;
1989
1990 deselectFlagList.clear();
1991 disableFlagList.clear();
1992 disableFlagList << 0 << 1 << 2 << 3 << 5 << 6 << 7;
1993 keyMovementList.clear();
1994 QTest::newRow(dataTag: testCase.toLatin1() + "only one enabled") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 4;
1995
1996 if (!editable) {
1997 deselectFlagList.clear();
1998 disableFlagList.clear();
1999 keyMovementList.clear();
2000 disableFlagList << 0 << 2 << 3;
2001 keyMovementList << Qt::Key_Down << Qt::Key_Home;
2002 QTest::newRow(dataTag: testCase.toLatin1() + "home-disabled") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 1;
2003
2004 keyMovementList.clear();
2005 keyMovementList << Qt::Key_End;
2006 QTest::newRow(dataTag: testCase.toLatin1() + "end-key") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 7;
2007
2008 disableFlagList.clear();
2009 disableFlagList << 1 ;
2010 keyMovementList << Qt::Key_T;
2011 QTest::newRow(dataTag: testCase.toLatin1() + "keyboard-search") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 2;
2012
2013 itemList << "nine" << "ten";
2014 keyMovementList << Qt::Key_T;
2015 QTest::newRow(dataTag: testCase.toLatin1() + "search same start letter") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 2;
2016
2017 keyMovementList.clear();
2018 keyMovementList << Qt::Key_T << Qt::Key_H;
2019 QTest::newRow(dataTag: testCase.toLatin1() + "keyboard search item") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 2;
2020
2021 disableFlagList.clear();
2022 disableFlagList << 1 << 3 << 5 << 7 << 9;
2023 keyMovementList.clear();
2024 keyMovementList << Qt::Key_End << Qt::Key_Up << Qt::Key_Up << Qt::Key_PageDown << Qt::Key_PageUp << Qt::Key_PageUp << Qt::Key_Down;
2025 QTest::newRow(dataTag: testCase.toLatin1() + "all key combinations") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 4;
2026 } else {
2027 deselectFlagList.clear();
2028 disableFlagList.clear();
2029 disableFlagList << 1;
2030 keyMovementList.clear();
2031 keyMovementList << Qt::Key_T << Qt::Key_Enter;
2032 QTest::newRow(dataTag: testCase.toLatin1() + "disabled") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 2;
2033 QTest::newRow(dataTag: testCase.toLatin1() + "broken autocompletion") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 2;
2034 }
2035 }
2036}
2037
2038void tst_QComboBox::flaggedItems()
2039{
2040 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
2041 QSKIP("Wayland: This fails. Figure out why.");
2042
2043 QFETCH(QStringList, itemList);
2044 QFETCH(IntList, deselectFlagList);
2045 QFETCH(IntList, disableFlagList);
2046 QFETCH(KeyList, keyMovementList);
2047 QFETCH(bool, editable);
2048 QFETCH(int, expectedIndex);
2049
2050 QComboBox comboBox;
2051 setFrameless(&comboBox);
2052 QListWidget listWidget;
2053 listWidget.addItems(labels: itemList);
2054
2055 comboBox.setEditable(editable);
2056 foreach (int index, deselectFlagList)
2057 listWidget.item(row: index)->setFlags(listWidget.item(row: index)->flags() & ~Qt::ItemIsSelectable);
2058
2059 foreach (int index, disableFlagList)
2060 listWidget.item(row: index)->setFlags(listWidget.item(row: index)->flags() & ~Qt::ItemIsEnabled);
2061
2062 comboBox.setModel(listWidget.model());
2063 comboBox.setView(&listWidget);
2064 comboBox.move(ax: 200, ay: 200);
2065 comboBox.show();
2066 QApplication::setActiveWindow(&comboBox);
2067 comboBox.activateWindow();
2068 comboBox.setFocus();
2069 QVERIFY(QTest::qWaitForWindowActive(&comboBox));
2070 QTRY_VERIFY(comboBox.isVisible());
2071 QTRY_VERIFY(comboBox.hasFocus());
2072
2073 if (editable)
2074 comboBox.lineEdit()->selectAll();
2075
2076 for (int i = 0; i < keyMovementList.count(); ++i) {
2077 Qt::Key key = keyMovementList[i];
2078 QTest::keyClick(widget: &comboBox, key);
2079 }
2080
2081 QCOMPARE(comboBox.currentIndex() , expectedIndex);
2082}
2083
2084void tst_QComboBox::pixmapIcon()
2085{
2086 QComboBox box;
2087 QStandardItemModel *model = new QStandardItemModel(2, 1, &box);
2088
2089 QPixmap pix(10, 10);
2090 pix.fill(fillColor: Qt::red);
2091 model->setData(index: model->index(row: 0, column: 0), value: "Element 1");
2092 model->setData(index: model->index(row: 0, column: 0), value: pix, role: Qt::DecorationRole);
2093
2094 QIcon icon(pix);
2095 model->setData(index: model->index(row: 1, column: 0), value: "Element 2");
2096 model->setData(index: model->index(row: 1, column: 0), value: icon, role: Qt::DecorationRole);
2097
2098 box.setModel(model);
2099
2100 QCOMPARE( box.itemIcon(0).isNull(), false );
2101 QCOMPARE( box.itemIcon(1).isNull(), false );
2102}
2103
2104#if QT_CONFIG(wheelevent)
2105// defined to be 120 by the wheel mouse vendors according to the docs
2106#define WHEEL_DELTA 120
2107
2108void tst_QComboBox::mouseWheel_data()
2109{
2110 QTest::addColumn<IntList>(name: "disabledItems");
2111 QTest::addColumn<int>(name: "startIndex");
2112 QTest::addColumn<int>(name: "wheelDirection");
2113 QTest::addColumn<int>(name: "expectedIndex");
2114
2115 IntList disabled;
2116 disabled << 0 << 1 << 2 << 4;
2117 int start = 3;
2118 int wheel = 1;
2119 int expected = 3;
2120 QTest::newRow(dataTag: "upper locked") << disabled << start << wheel << expected;
2121
2122 wheel = -1;
2123 const bool allowsWheelScroll = QApplication::style()->styleHint(stylehint: QStyle::SH_ComboBox_AllowWheelScrolling);
2124 // on OS X & iOS mouse wheel shall have no effect on combo box
2125 if (!allowsWheelScroll)
2126 expected = start;
2127 else // on other OSes we should jump to next enabled item (no. 5)
2128 expected = 5;
2129
2130 QTest::newRow(dataTag: "jump over") << disabled << start << wheel << expected;
2131
2132 disabled.clear();
2133 disabled << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9;
2134 start = 0;
2135 wheel = -1;
2136 expected = 0;
2137 QTest::newRow(dataTag: "single Item enabled") << disabled << start << wheel << expected;
2138}
2139
2140void tst_QComboBox::mouseWheel()
2141{
2142 QFETCH(IntList, disabledItems);
2143 QFETCH(int, startIndex);
2144 QFETCH(int, wheelDirection);
2145 QFETCH(int, expectedIndex);
2146
2147 QCoreApplication *applicationInstance = QCoreApplication::instance();
2148 QVERIFY(applicationInstance != 0);
2149
2150 QComboBox box;
2151 QStringList list;
2152 list << "one" << "two" << "three" << "four" << "five" << "six" << "seven" << "eight" << "nine" << "ten";
2153
2154 QListWidget listWidget;
2155 listWidget.addItems(labels: list);
2156
2157 foreach (int index, disabledItems)
2158 listWidget.item(row: index)->setFlags(listWidget.item(row: index)->flags() & ~Qt::ItemIsEnabled);
2159
2160 box.setModel(listWidget.model());
2161 box.setView(&listWidget);
2162 for (int i=0; i < 2; ++i) {
2163 box.setEditable(i==0?false:true);
2164 box.setCurrentIndex(startIndex);
2165
2166 const QPoint wheelPoint = box.rect().bottomRight();
2167 QWheelEvent event(wheelPoint, box.mapToGlobal(wheelPoint), QPoint(), QPoint(0, WHEEL_DELTA * wheelDirection),
2168 Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
2169 QVERIFY(applicationInstance->sendEvent(&box,&event));
2170
2171 QCOMPARE(box.currentIndex(), expectedIndex);
2172 }
2173}
2174
2175void tst_QComboBox::popupWheelHandling()
2176{
2177 // QTBUG-40656, QTBUG-42731 combo and other popups should not be affected by wheel events.
2178 QScrollArea scrollArea;
2179 scrollArea.move(ax: 300, ay: 300);
2180 QWidget *widget = new QWidget;
2181 scrollArea.setWidget(widget);
2182 QVBoxLayout *layout = new QVBoxLayout(widget);
2183 layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
2184 layout->addSpacing(size: 100);
2185 QComboBox *comboBox = new QComboBox;
2186 comboBox->addItems(texts: QStringList() << QStringLiteral("Won") << QStringLiteral("Too")
2187 << QStringLiteral("3") << QStringLiteral("fore"));
2188 layout->addWidget(comboBox);
2189 layout->addSpacing(size: 100);
2190 const QPoint sizeP(scrollArea.width(), scrollArea.height());
2191 scrollArea.move(QGuiApplication::primaryScreen()->availableGeometry().center() - sizeP / 2);
2192 scrollArea.show();
2193 QVERIFY(QTest::qWaitForWindowExposed(&scrollArea));
2194 comboBox->showPopup();
2195 QTRY_VERIFY(comboBox->view() && comboBox->view()->isVisible());
2196 const QPoint popupPos = comboBox->view()->pos();
2197 const QPoint wheelPoint(10, 10);
2198 QWheelEvent event(wheelPoint, scrollArea.mapToGlobal(wheelPoint), QPoint(), QPoint(0, WHEEL_DELTA),
2199 Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
2200 QVERIFY(QCoreApplication::sendEvent(scrollArea.windowHandle(), &event));
2201 QCoreApplication::processEvents();
2202 QVERIFY(comboBox->view()->isVisible());
2203 QCOMPARE(comboBox->view()->pos(), popupPos);
2204}
2205#endif // QT_CONFIG(wheelevent)
2206
2207void tst_QComboBox::layoutDirection()
2208{
2209 QComboBox box;
2210 Qt::LayoutDirection dir;
2211 QLineEdit *lineEdit;
2212
2213 // RTL
2214 box.setLayoutDirection(Qt::RightToLeft);
2215 QStyleOptionComboBox opt;
2216 opt.direction = Qt::RightToLeft;
2217 dir = (Qt::LayoutDirection)box.style()->styleHint(stylehint: QStyle::SH_ComboBox_LayoutDirection, opt: &opt, widget: &box);
2218
2219 QCOMPARE(box.view()->layoutDirection(), dir);
2220 box.setEditable(true);
2221 QCOMPARE(box.lineEdit()->layoutDirection(), dir);
2222 lineEdit = new QLineEdit;
2223 QCOMPARE(lineEdit->layoutDirection(), qApp->layoutDirection());
2224 box.setLineEdit(lineEdit);
2225 QCOMPARE(lineEdit->layoutDirection(), dir);
2226
2227 // LTR
2228 box.setLayoutDirection(Qt::LeftToRight);
2229 qApp->setLayoutDirection(Qt::RightToLeft);
2230
2231 opt.direction = Qt::LeftToRight;
2232 dir = (Qt::LayoutDirection)box.style()->styleHint(stylehint: QStyle::SH_ComboBox_LayoutDirection, opt: &opt, widget: &box);
2233
2234 QCOMPARE(box.view()->layoutDirection(), dir);
2235 box.setEditable(true);
2236 QCOMPARE(box.lineEdit()->layoutDirection(), dir);
2237 lineEdit = new QLineEdit;
2238 QCOMPARE(lineEdit->layoutDirection(), qApp->layoutDirection());
2239 box.setLineEdit(lineEdit);
2240 QCOMPARE(lineEdit->layoutDirection(), dir);
2241
2242}
2243
2244void tst_QComboBox::itemListPosition()
2245{
2246 //tests that the list is not out of the screen boundaries
2247
2248 //put the QApplication layout back
2249 QApplication::setLayoutDirection(Qt::LeftToRight);
2250
2251 //we test QFontComboBox because it has the specific behaviour to set a fixed size
2252 //to the list view
2253 QWidget topLevel;
2254 QHBoxLayout *layout = new QHBoxLayout(&topLevel);
2255
2256 QFontComboBox combo(&topLevel);
2257
2258 layout->addWidget(&combo);
2259
2260 bool useFullScreenForPopupMenu = false;
2261 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme())
2262 useFullScreenForPopupMenu = theme->themeHint(hint: QPlatformTheme::UseFullScreenForPopupMenu).toBool();
2263 const QRect screen = useFullScreenForPopupMenu ?
2264 combo.screen()->geometry() :
2265 combo.screen()->availableGeometry();
2266
2267 topLevel.move(ax: screen.width() - topLevel.sizeHint().width() - 10, ay: 0); //puts the combo to the top-right corner
2268
2269 topLevel.showNormal();
2270
2271 //wait because the window manager can move the window if there is a right panel
2272 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
2273 combo.showPopup();
2274 QTRY_VERIFY(combo.view());
2275 QTRY_VERIFY(combo.view()->isVisible());
2276 QVERIFY( combo.view()->window()->x() + combo.view()->window()->width() <= screen.x() + screen.width() );
2277}
2278
2279void tst_QComboBox::separatorItem_data()
2280{
2281 QTest::addColumn<QStringList>(name: "items");
2282 QTest::addColumn<IntList>(name: "separators");
2283
2284 QTest::newRow(dataTag: "test") << (QStringList() << "one" << "two" << "three" << "other...")
2285 << (IntList() << 4);
2286}
2287
2288void tst_QComboBox::separatorItem()
2289{
2290 QFETCH(QStringList, items);
2291 QFETCH(IntList, separators);
2292
2293 QComboBox box;
2294 box.addItems(texts: items);
2295 foreach(int index, separators)
2296 box.insertSeparator(index);
2297 QCOMPARE(box.count(), (items.count() + separators.count()));
2298 for (int i = 0, s = 0; i < box.count(); ++i) {
2299 if (i == separators.at(i: s)) {
2300 QCOMPARE(box.itemText(i), QString());
2301 ++s;
2302 } else {
2303 QCOMPARE(box.itemText(i), items.at(i - s));
2304 }
2305 }
2306}
2307
2308// This test requires the Fusionstyle
2309#ifndef QT_NO_STYLE_FUSION
2310void tst_QComboBox::task190351_layout()
2311{
2312 const QString oldStyle = QApplication::style()->objectName();
2313 QApplication::setStyle(QStyleFactory::create(QLatin1String("Fusion")));
2314
2315 QComboBox listCombo;
2316 listCombo.move(ax: 200, ay: 200);
2317 setFrameless(&listCombo);
2318 QListWidget *list = new QListWidget();
2319 listCombo.setModel(list->model());
2320 listCombo.setView(list);
2321 for(int i = 1; i < 150; i++)
2322 list->addItem(label: QLatin1String("list") + QString::number(i));
2323
2324 listCombo.show();
2325 QVERIFY(QTest::qWaitForWindowExposed(&listCombo));
2326 QTRY_VERIFY(listCombo.isVisible());
2327 listCombo.setCurrentIndex(70);
2328 listCombo.showPopup();
2329 QTRY_VERIFY(listCombo.view());
2330 QVERIFY(QTest::qWaitForWindowExposed(listCombo.view()));
2331 QTRY_VERIFY(listCombo.view()->isVisible());
2332 QApplication::processEvents();
2333
2334#ifdef QT_BUILD_INTERNAL
2335 QFrame *container = listCombo.findChild<QComboBoxPrivateContainer *>();
2336 QVERIFY(container);
2337 QCOMPARE(static_cast<QAbstractItemView *>(list), container->findChild<QAbstractItemView *>());
2338 QWidget *top = container->findChild<QComboBoxPrivateScroller *>();
2339 QVERIFY(top);
2340 QVERIFY(top->isVisible());
2341 QCOMPARE(top->mapToGlobal(QPoint(0, top->height())).y(), list->mapToGlobal(QPoint()).y());
2342#endif
2343
2344 QApplication::setStyle(oldStyle);
2345}
2346#endif
2347
2348class task166349_ComboBox : public QComboBox
2349{
2350 Q_OBJECT
2351public:
2352 task166349_ComboBox(QWidget *parent = 0) : QComboBox(parent)
2353 {
2354 QStringList list;
2355 list << "one" << "two";
2356 connect(sender: this, SIGNAL(currentIndexChanged(int)), receiver: this, SLOT(onCurrentIndexChanged(int)));
2357 addItems(texts: list);
2358 }
2359public slots:
2360 void onCurrentIndexChanged(int index)
2361 {
2362 setEditable(index % 2 == 1);
2363 }
2364};
2365
2366void tst_QComboBox::task166349_setEditableOnReturn()
2367{
2368 task166349_ComboBox comboBox;
2369 QTest::keyClick(widget: &comboBox, key: Qt::Key_Down);
2370 QTest::keyClick(widget: &comboBox, key: Qt::Key_1);
2371 QTest::keyClick(widget: &comboBox, key: Qt::Key_Enter);
2372 QCOMPARE(QLatin1String("two1"), comboBox.itemText(comboBox.count() - 1));
2373}
2374
2375// This test requires the Fusion style.
2376#ifndef QT_NO_STYLE_FUSION
2377void tst_QComboBox::task191329_size()
2378{
2379 const QString oldStyle = QApplication::style()->objectName();
2380 QApplication::setStyle(QStyleFactory::create(QLatin1String("Fusion")));
2381
2382
2383 QComboBox tableCombo;
2384 setFrameless(&tableCombo);
2385 tableCombo.move(ax: 200, ay: 200);
2386 int rows;
2387 if (QApplication::primaryScreen()->geometry().height() < 480)
2388 rows = 8;
2389 else
2390 rows = 15;
2391
2392 QStandardItemModel model(rows, 2);
2393 for (int row = 0; row < model.rowCount(); ++row) {
2394 const QString rowS = QLatin1String("row ") + QString::number(row);
2395 for (int column = 0; column < model.columnCount(); ++column) {
2396 const QString text = rowS + QLatin1String(", column ") + QString::number(column);
2397 model.setItem(row, column, item: new QStandardItem(text));
2398 }
2399 }
2400 QTableView *table = new QTableView();
2401 table->verticalHeader()->hide();
2402 table->horizontalHeader()->hide();
2403 tableCombo.setView(table);
2404 tableCombo.setModel(&model);
2405
2406 tableCombo.show();
2407 QTRY_VERIFY(tableCombo.isVisible());
2408 tableCombo.showPopup();
2409 QTRY_VERIFY(tableCombo.view());
2410 QTRY_VERIFY(tableCombo.view()->isVisible());
2411
2412#ifdef QT_BUILD_INTERNAL
2413 QFrame *container = tableCombo.findChild<QComboBoxPrivateContainer *>();
2414 QVERIFY(container);
2415 QCOMPARE(static_cast<QAbstractItemView *>(table), container->findChild<QAbstractItemView *>());
2416 foreach (QWidget *button, container->findChildren<QComboBoxPrivateScroller *>()) {
2417 //the popup should be large enough to contains everithing so the top and left button are hidden
2418 QVERIFY(!button->isVisible());
2419 }
2420#endif
2421
2422 QApplication::setStyle(oldStyle);
2423}
2424#endif
2425
2426void tst_QComboBox::task190205_setModelAdjustToContents()
2427{
2428 QStringList initialContent;
2429 QStringList finalContent;
2430 initialContent << "foo" << "bar";
2431 finalContent << "bar" << "foooooooobar";
2432
2433 QComboBox box;
2434 setFrameless(&box);
2435 box.move(ax: 100, ay: 100);
2436 box.setSizeAdjustPolicy(QComboBox::AdjustToContents);
2437 box.addItems(texts: initialContent);
2438 box.showNormal();
2439
2440 //wait needed in order to get the combo initial size
2441 QTRY_VERIFY(box.isVisible());
2442
2443 box.setModel(new QStringListModel(finalContent));
2444
2445 QComboBox correctBox;
2446 setFrameless(&correctBox);
2447 correctBox.move(ax: 400, ay: 100);
2448
2449 correctBox.addItems(texts: finalContent);
2450 correctBox.showNormal();
2451
2452#ifdef Q_OS_WINRT
2453 QEXPECT_FAIL("", "WinRT does not support more than 1 native top level widget", Abort);
2454#endif
2455 QVERIFY(QTest::qWaitForWindowExposed(&box));
2456 QVERIFY(QTest::qWaitForWindowExposed(&correctBox));
2457
2458 // box should be resized to the same size as correctBox
2459 QTRY_COMPARE(box.size(), correctBox.size());
2460}
2461
2462void tst_QComboBox::task248169_popupWithMinimalSize()
2463{
2464 QStringList initialContent;
2465 initialContent << "foo" << "bar" << "foobar";
2466
2467 QComboBox comboBox;
2468 comboBox.addItems(texts: initialContent);
2469 QRect desktopSize = QGuiApplication::primaryScreen()->availableGeometry();
2470 comboBox.view()->setMinimumWidth(desktopSize.width() / 2);
2471
2472 comboBox.setGeometry(ax: desktopSize.width() - (desktopSize.width() / 4), ay: (desktopSize.width() / 4), aw: (desktopSize.width() / 2), ah: (desktopSize.width() / 4));
2473
2474 comboBox.showNormal();
2475 QVERIFY(QTest::qWaitForWindowExposed(&comboBox));
2476 QTRY_VERIFY(comboBox.isVisible());
2477 comboBox.showPopup();
2478 QTRY_VERIFY(comboBox.view());
2479 QVERIFY(QTest::qWaitForWindowExposed(comboBox.view()));
2480 QTRY_VERIFY(comboBox.view()->isVisible());
2481
2482#if defined QT_BUILD_INTERNAL
2483 QFrame *container = comboBox.findChild<QComboBoxPrivateContainer *>();
2484 QVERIFY(container);
2485 QTRY_VERIFY(container->screen()->geometry().contains(container->geometry()));
2486#endif
2487}
2488
2489void tst_QComboBox::task247863_keyBoardSelection()
2490{
2491 QComboBox combo;
2492 setFrameless(&combo);
2493 combo.move(ax: 200, ay: 200);
2494 combo.setEditable(false);
2495 combo.addItem( atext: QLatin1String("111"));
2496 combo.addItem( atext: QLatin1String("222"));
2497 combo.show();
2498 QApplication::setActiveWindow(&combo);
2499 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&combo));
2500
2501 QSignalSpy spy(&combo, SIGNAL(activated(QString)));
2502 qApp->setEffectEnabled(Qt::UI_AnimateCombo, enable: false);
2503 QTest::keyClick(widget: &combo, key: Qt::Key_Space);
2504 qApp->setEffectEnabled(Qt::UI_AnimateCombo, enable: true);
2505 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Down);
2506 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Enter);
2507 QCOMPARE(combo.currentText(), QLatin1String("222"));
2508 QCOMPARE(spy.count(), 1);
2509}
2510
2511void tst_QComboBox::task220195_keyBoardSelection2()
2512{
2513 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
2514 QSKIP("Wayland: This fails. Figure out why.");
2515
2516 QComboBox combo;
2517 setFrameless(&combo);
2518 combo.move(ax: 200, ay: 200);
2519 combo.setEditable(false);
2520 combo.addItem( atext: QLatin1String("foo1"));
2521 combo.addItem( atext: QLatin1String("foo2"));
2522 combo.addItem( atext: QLatin1String("foo3"));
2523 combo.show();
2524 QApplication::setActiveWindow(&combo);
2525 QVERIFY(QTest::qWaitForWindowActive(&combo));
2526
2527 combo.setCurrentIndex(-1);
2528 QVERIFY(combo.currentText().isNull());
2529
2530 QTest::keyClick(widget: &combo, key: 'f');
2531 QCOMPARE(combo.currentText(), QLatin1String("foo1"));
2532 QTest::qWait(ms: QApplication::keyboardInputInterval() + 30);
2533 QTest::keyClick(widget: &combo, key: 'f');
2534 QCOMPARE(combo.currentText(), QLatin1String("foo2"));
2535 QTest::qWait(ms: QApplication::keyboardInputInterval() + 30);
2536 QTest::keyClick(widget: &combo, key: 'f');
2537 QCOMPARE(combo.currentText(), QLatin1String("foo3"));
2538 QTest::qWait(ms: QApplication::keyboardInputInterval() + 30);
2539 QTest::keyClick(widget: &combo, key: 'f');
2540 QCOMPARE(combo.currentText(), QLatin1String("foo1"));
2541 QTest::qWait(ms: QApplication::keyboardInputInterval() + 30);
2542
2543 combo.setCurrentIndex(1);
2544 QCOMPARE(combo.currentText(), QLatin1String("foo2"));
2545 QTest::keyClick(widget: &combo, key: 'f');
2546 QCOMPARE(combo.currentText(), QLatin1String("foo3"));
2547}
2548
2549
2550void tst_QComboBox::setModelColumn()
2551{
2552 QStandardItemModel model(5,3);
2553 model.setItem(row: 0,column: 0, item: new QStandardItem("0"));
2554 model.setItem(row: 1,column: 0, item: new QStandardItem("1"));
2555 model.setItem(row: 2,column: 0, item: new QStandardItem("2"));
2556 model.setItem(row: 3,column: 0, item: new QStandardItem("3"));
2557 model.setItem(row: 4,column: 0, item: new QStandardItem("4"));
2558 model.setItem(row: 0,column: 1, item: new QStandardItem("zero"));
2559 model.setItem(row: 1,column: 1, item: new QStandardItem("un"));
2560 model.setItem(row: 2,column: 1, item: new QStandardItem("deux"));
2561 model.setItem(row: 3,column: 1, item: new QStandardItem("trois"));
2562 model.setItem(row: 4,column: 1, item: new QStandardItem("quatre"));
2563 model.setItem(row: 0,column: 2, item: new QStandardItem("a"));
2564 model.setItem(row: 1,column: 2, item: new QStandardItem("b"));
2565 model.setItem(row: 2,column: 2, item: new QStandardItem("c"));
2566 model.setItem(row: 3,column: 2, item: new QStandardItem("d"));
2567 model.setItem(row: 4,column: 2, item: new QStandardItem("e"));
2568
2569 QComboBox box;
2570 box.setModel(&model);
2571 QCOMPARE(box.currentText(), QString("0"));
2572 box.setModelColumn(1);
2573 QCOMPARE(box.currentText(), QString("zero"));
2574}
2575
2576void tst_QComboBox::noScrollbar_data()
2577{
2578 QTest::addColumn<QString>(name: "stylesheet");
2579
2580 QTest::newRow(dataTag: "normal") << QString();
2581 QTest::newRow(dataTag: "border") << QString::fromLatin1(str: "QAbstractItemView { border: 12px solid blue;}");
2582 QTest::newRow(dataTag: "margin") << QString::fromLatin1(str: "QAbstractItemView { margin: 12px 15px 13px 10px; }");
2583 QTest::newRow(dataTag: "padding") << QString::fromLatin1(str: "QAbstractItemView { padding: 12px 15px 13px 10px;}");
2584 QTest::newRow(dataTag: "everything") << QString::fromLatin1(str: "QAbstractItemView { border: 12px solid blue; "
2585 " padding: 12px 15px 13px 10px; margin: 12px 15px 13px 10px; }");
2586 QTest::newRow(dataTag: "everything and more") << QString::fromLatin1(str: "QAbstractItemView { border: 1px 3px 5px 1px solid blue; "
2587 " padding: 2px 5px 3px 1px; margin: 2px 5px 3px 1px; } "
2588 " QAbstractItemView::item { border: 2px solid green; "
2589 " padding: 1px 1px 2px 2px; margin: 1px; } " );
2590}
2591
2592void tst_QComboBox::noScrollbar()
2593{
2594 QStringList initialContent;
2595 initialContent << "foo" << "bar" << "foobar" << "moo";
2596 QFETCH(QString, stylesheet);
2597 QString oldCss = qApp->styleSheet();
2598 qApp->setStyleSheet(stylesheet);
2599
2600 {
2601 QWidget topLevel;
2602 QComboBox comboBox(&topLevel);
2603 comboBox.addItems(texts: initialContent);
2604 topLevel.move(ax: 200, ay: 200);
2605 topLevel.show();
2606 comboBox.resize(w: 200, h: comboBox.height());
2607 QTRY_VERIFY(comboBox.isVisible());
2608 comboBox.showPopup();
2609 QTRY_VERIFY(comboBox.view());
2610 QTRY_VERIFY(comboBox.view()->isVisible());
2611
2612 QVERIFY(!comboBox.view()->horizontalScrollBar()->isVisible());
2613 QVERIFY(!comboBox.view()->verticalScrollBar()->isVisible());
2614 }
2615
2616 {
2617 QTableWidget *table = new QTableWidget(2,2);
2618 QComboBox comboBox;
2619 comboBox.setModel(table->model());
2620 comboBox.setView(table);
2621 comboBox.move(ax: 200, ay: 200);
2622 comboBox.show();
2623 QTRY_VERIFY(comboBox.isVisible());
2624 comboBox.resize(w: 200, h: comboBox.height());
2625 comboBox.showPopup();
2626 QTRY_VERIFY(comboBox.view());
2627 QTRY_VERIFY(comboBox.view()->isVisible());
2628
2629 QVERIFY(!comboBox.view()->horizontalScrollBar()->isVisible());
2630 QVERIFY(!comboBox.view()->verticalScrollBar()->isVisible());
2631 }
2632
2633 qApp->setStyleSheet(oldCss);
2634}
2635
2636void tst_QComboBox::setItemDelegate()
2637{
2638 QComboBox comboBox;
2639 QStyledItemDelegate *itemDelegate = new QStyledItemDelegate;
2640 comboBox.setItemDelegate(itemDelegate);
2641 // the cast is a workaround for the XLC and Metrowerks compilers
2642 QCOMPARE(static_cast<QStyledItemDelegate *>(comboBox.itemDelegate()), itemDelegate);
2643}
2644
2645void tst_QComboBox::task253944_itemDelegateIsReset()
2646{
2647 QComboBox comboBox;
2648 QStyledItemDelegate *itemDelegate = new QStyledItemDelegate;
2649 comboBox.setItemDelegate(itemDelegate);
2650
2651 // the casts are workarounds for the XLC and Metrowerks compilers
2652
2653 comboBox.setEditable(true);
2654 QCOMPARE(static_cast<QStyledItemDelegate *>(comboBox.itemDelegate()), itemDelegate);
2655
2656 comboBox.setStyleSheet("QComboBox { border: 1px solid gray; }");
2657 QCOMPARE(static_cast<QStyledItemDelegate *>(comboBox.itemDelegate()), itemDelegate);
2658}
2659
2660
2661void tst_QComboBox::subControlRectsWithOffset_data()
2662{
2663 QTest::addColumn<bool>(name: "editable");
2664
2665 QTest::newRow(dataTag: "editable = true") << true;
2666 QTest::newRow(dataTag: "editable = false") << false;
2667}
2668
2669void tst_QComboBox::subControlRectsWithOffset()
2670{
2671 // The sub control rect relative position should not depends
2672 // on the position of the combobox
2673
2674 class FriendlyCombo : public QComboBox {
2675 public:
2676 void styleOption(QStyleOptionComboBox *optCombo) {
2677 initStyleOption(option: optCombo);
2678 }
2679 } combo;
2680 QStyleOptionComboBox optCombo;
2681 combo.styleOption(optCombo: &optCombo);
2682
2683
2684 const QRect rectAtOrigin(0, 0, 80, 30);
2685 const QPoint offset(25, 50);
2686 const QRect rectWithOffset = rectAtOrigin.translated(p: offset);
2687
2688 QStyle *style = combo.style();
2689
2690 QFETCH(bool, editable);
2691 optCombo.editable = editable;
2692
2693 optCombo.rect = rectAtOrigin;
2694 QRect editFieldRect = style->subControlRect(cc: QStyle::CC_ComboBox, opt: &optCombo, sc: QStyle::SC_ComboBoxEditField, widget: 0);
2695 QRect arrowRect = style->subControlRect(cc: QStyle::CC_ComboBox, opt: &optCombo, sc: QStyle::SC_ComboBoxArrow, widget: 0);
2696 QRect listboxRect = style->subControlRect(cc: QStyle::CC_ComboBox, opt: &optCombo, sc: QStyle::SC_ComboBoxListBoxPopup, widget: 0);
2697
2698 optCombo.rect = rectWithOffset;
2699 QRect editFieldRectWithOffset = style->subControlRect(cc: QStyle::CC_ComboBox, opt: &optCombo, sc: QStyle::SC_ComboBoxEditField, widget: 0);
2700 QRect arrowRectWithOffset = style->subControlRect(cc: QStyle::CC_ComboBox, opt: &optCombo, sc: QStyle::SC_ComboBoxArrow, widget: 0);
2701 QRect listboxRectWithOffset = style->subControlRect(cc: QStyle::CC_ComboBox, opt: &optCombo, sc: QStyle::SC_ComboBoxListBoxPopup, widget: 0);
2702
2703 QCOMPARE(editFieldRect, editFieldRectWithOffset.translated(-offset));
2704 QCOMPARE(arrowRect, arrowRectWithOffset.translated(-offset));
2705 QCOMPARE(listboxRect, listboxRectWithOffset.translated(-offset));
2706
2707}
2708
2709// This test depends on Windows style.
2710#ifndef QT_NO_STYLE_WINDOWS
2711void tst_QComboBox::task260974_menuItemRectangleForComboBoxPopup()
2712{
2713 class TestStyle: public QProxyStyle
2714 {
2715 public:
2716 TestStyle() : QProxyStyle(QStyleFactory::create("windows")) { }
2717
2718 int styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *ret) const
2719 {
2720 if (hint == SH_ComboBox_Popup) return 1;
2721 else return QCommonStyle::styleHint(sh: hint, opt: option, w: widget, shret: ret);
2722 }
2723
2724 void drawControl(ControlElement element, const QStyleOption *option, QPainter *, const QWidget *) const
2725 {
2726 if (element == CE_MenuItem)
2727 discoveredRect = option->rect;
2728 }
2729
2730 mutable QRect discoveredRect;
2731 } style;
2732
2733
2734 {
2735 QComboBox comboBox;
2736 comboBox.setStyle(&style);
2737 comboBox.addItem(atext: "Item 1");
2738 comboBox.move(ax: 200, ay: 200);
2739
2740 comboBox.show();
2741 QTRY_VERIFY(comboBox.isVisible());
2742 comboBox.showPopup();
2743 QTRY_VERIFY(comboBox.view());
2744 QTRY_VERIFY(comboBox.view()->isVisible());
2745
2746 QTRY_VERIFY(style.discoveredRect.width() <= comboBox.width());
2747 }
2748}
2749#endif
2750
2751void tst_QComboBox::removeItem()
2752{
2753 QComboBox cb;
2754 cb.removeItem(index: -1);
2755 cb.removeItem(index: 1);
2756 cb.removeItem(index: 0);
2757 QCOMPARE(cb.count(), 0);
2758
2759 cb.addItem(atext: "foo");
2760 cb.removeItem(index: -1);
2761 QCOMPARE(cb.count(), 1);
2762 cb.removeItem(index: 1);
2763 QCOMPARE(cb.count(), 1);
2764 cb.removeItem(index: 0);
2765 QCOMPARE(cb.count(), 0);
2766}
2767
2768void tst_QComboBox::resetModel()
2769{
2770 class StringListModel : public QStringListModel
2771 {
2772 public:
2773 using QStringListModel::QStringListModel;
2774 void reset()
2775 {
2776 QStringListModel::beginResetModel();
2777 QStringListModel::endResetModel();
2778 }
2779 };
2780 QComboBox cb;
2781 StringListModel model({"1", "2"});
2782 QSignalSpy spy(&cb, QOverload<int>::of(ptr: &QComboBox::currentIndexChanged));
2783 QCOMPARE(spy.count(), 0);
2784 QCOMPARE(cb.currentIndex(), -1); //no selection
2785
2786 cb.setModel(&model);
2787
2788 QCOMPARE(spy.count(), 1);
2789 QCOMPARE(cb.currentIndex(), 0); //first item selected
2790
2791 model.reset();
2792 QCOMPARE(spy.count(), 2);
2793 QCOMPARE(cb.currentIndex(), 0); //first item selected
2794
2795}
2796
2797void tst_QComboBox::keyBoardNavigationWithMouse()
2798{
2799 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
2800 QSKIP("Wayland: This fails. Figure out why.");
2801
2802 QComboBox combo;
2803 combo.setEditable(false);
2804 setFrameless(&combo);
2805 for (int i = 0; i < 200; i++)
2806 combo.addItem(atext: QString::number(i));
2807
2808 combo.move(ax: 200, ay: 200);
2809 combo.showNormal();
2810 QApplication::setActiveWindow(&combo);
2811 QVERIFY(QTest::qWaitForWindowActive(&combo));
2812
2813 QCOMPARE(combo.currentText(), QLatin1String("0"));
2814
2815 combo.setFocus();
2816 QTRY_VERIFY(combo.hasFocus());
2817
2818 QTest::keyClick(widget: combo.lineEdit(), key: Qt::Key_Space);
2819 QTRY_VERIFY(combo.view());
2820 QTRY_VERIFY(combo.view()->isVisible());
2821
2822 QCOMPARE(combo.currentText(), QLatin1String("0"));
2823
2824 QTest::mouseMove(widget: &combo, pos: combo.rect().center());
2825
2826#define GET_SELECTION(SEL) \
2827 QCOMPARE(combo.view()->selectionModel()->selection().count(), 1); \
2828 QCOMPARE(combo.view()->selectionModel()->selection().indexes().count(), 1); \
2829 SEL = combo.view()->selectionModel()->selection().indexes().first().row()
2830
2831 int selection;
2832 GET_SELECTION(selection); // get initial selection
2833
2834 const int final = 40;
2835 for (int i = selection + 1; i <= final; i++)
2836 {
2837 QTest::keyClick(widget: combo.view(), key: Qt::Key_Down);
2838 GET_SELECTION(selection);
2839 QCOMPARE(selection, i);
2840 }
2841
2842 QTest::keyClick(widget: combo.view(), key: Qt::Key_Enter);
2843 QTRY_COMPARE(combo.currentText(), QString::number(final));
2844#undef GET_SELECTION
2845}
2846
2847void tst_QComboBox::task_QTBUG_1071_changingFocusEmitsActivated()
2848{
2849 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
2850 QSKIP("Wayland: This fails. Figure out why.");
2851
2852 QWidget w;
2853 w.move(ax: 200, ay: 200);
2854 QVBoxLayout layout(&w);
2855 QComboBox cb;
2856 cb.setEditable(true);
2857 QSignalSpy spy(&cb, SIGNAL(activated(int)));
2858 cb.addItem(atext: "0");
2859 cb.addItem(atext: "1");
2860 cb.addItem(atext: "2");
2861 QLineEdit edit;
2862 layout.addWidget(&cb);
2863 layout.addWidget(&edit);
2864
2865 w.show();
2866 QApplication::setActiveWindow(&w);
2867 QVERIFY(QTest::qWaitForWindowActive(&w));
2868 cb.clearEditText();
2869 cb.setFocus();
2870 QApplication::processEvents();
2871 QTRY_VERIFY(cb.hasFocus());
2872 QTest::keyClick(widget: static_cast<QWidget *>(0), key: '1');
2873 QCOMPARE(spy.count(), 0);
2874 edit.setFocus();
2875 QTRY_VERIFY(edit.hasFocus());
2876 QTRY_COMPARE(spy.count(), 1);
2877}
2878
2879void tst_QComboBox::maxVisibleItems_data()
2880{
2881 QTest::addColumn<int>(name: "spacing");
2882 QTest::newRow(dataTag: "Default") << -1;
2883 QTest::newRow(dataTag: "No spacing") << 0;
2884 QTest::newRow(dataTag: "20") << -1;
2885}
2886
2887void tst_QComboBox::maxVisibleItems()
2888{
2889 QFETCH(int, spacing);
2890
2891 QComboBox comboBox;
2892 QCOMPARE(comboBox.maxVisibleItems(), 10); //default value.
2893
2894 QStringList content;
2895 for(int i = 1; i < 50; i++)
2896 content += QString::number(i);
2897
2898 comboBox.addItems(texts: content);
2899 comboBox.move(ax: 200, ay: 200);
2900 comboBox.show();
2901 comboBox.resize(w: 200, h: comboBox.height());
2902 QTRY_VERIFY(comboBox.isVisible());
2903
2904 comboBox.setMaxVisibleItems(5);
2905 QCOMPARE(comboBox.maxVisibleItems(), 5);
2906
2907 comboBox.showPopup();
2908 QTRY_VERIFY(comboBox.view());
2909 QTRY_VERIFY(comboBox.view()->isVisible());
2910
2911 QListView *listView = qobject_cast<QListView*>(object: comboBox.view());
2912 QVERIFY(listView);
2913 if (spacing >= 0)
2914 listView->setSpacing(spacing);
2915
2916 const int itemHeight = listView->visualRect(index: listView->model()->index(row: 0,column: 0)).height()
2917 + 2 * listView->spacing();
2918
2919 QStyleOptionComboBox opt;
2920 opt.initFrom(w: &comboBox);
2921 if (!comboBox.style()->styleHint(stylehint: QStyle::SH_ComboBox_Popup, opt: &opt))
2922 QCOMPARE(listView->viewport()->height(), itemHeight * comboBox.maxVisibleItems());
2923}
2924
2925void tst_QComboBox::task_QTBUG_10491_currentIndexAndModelColumn()
2926{
2927 QComboBox comboBox;
2928
2929 QStandardItemModel model(4, 4, &comboBox);
2930 for (int i = 0; i < 4; i++){
2931 const QString iS = QString::number(i);
2932 model.setItem(row: i, column: 0, item: new QStandardItem(QLatin1String("Employee Nr ") + iS));
2933 model.setItem(row: i, column: 1, item: new QStandardItem(QLatin1String("Street Nr ") + iS));
2934 model.setItem(row: i, column: 2, item: new QStandardItem(QLatin1String("Town Nr ") + iS));
2935 model.setItem(row: i, column: 3, item: new QStandardItem(QLatin1String("Phone Nr ") + iS));
2936 }
2937 comboBox.setModel(&model);
2938 comboBox.setModelColumn(0);
2939
2940 QComboBoxPrivate *d = static_cast<QComboBoxPrivate *>(QComboBoxPrivate::get(w: &comboBox));
2941 d->setCurrentIndex(model.index(row: 2, column: 2));
2942 QCOMPARE(QModelIndex(d->currentIndex), model.index(2, comboBox.modelColumn()));
2943}
2944
2945void tst_QComboBox::highlightedSignal()
2946{
2947 QComboBox comboBox;
2948
2949 QSignalSpy spy(&comboBox, SIGNAL(highlighted(int)));
2950 QVERIFY(spy.isValid());
2951
2952 // Calling view() before setting the model causes the creation
2953 // of a QComboBoxPrivateContainer containing an actual view, and connecting to
2954 // the selectionModel to generate the highlighted signal. When setModel is called
2955 // further down, that selectionModel is obsolete. We test that the highlighted
2956 // signal is emitted anyway as the bug fix. (QTBUG-4454)
2957 comboBox.view();
2958 QItemSelectionModel *initialItemSelectionModel = comboBox.view()->selectionModel();
2959
2960
2961 QStandardItemModel model;
2962 for (int i = 0; i < 5; i++)
2963 model.appendRow(aitem: new QStandardItem(QString::number(i)));
2964 comboBox.setModel(&model);
2965
2966 comboBox.view()->selectionModel()->setCurrentIndex(index: model.index(row: 0, column: 0), command: QItemSelectionModel::Current);
2967
2968 QVERIFY(initialItemSelectionModel != comboBox.view()->selectionModel());
2969
2970 QCOMPARE(spy.size(), 1);
2971}
2972
2973void tst_QComboBox::itemData()
2974{
2975 QComboBox comboBox;
2976 const int itemCount = 10;
2977
2978 // ensure that the currentText(), the DisplayRole and the EditRole
2979 // stay in sync when using QComboBox's default model
2980 for (int i = 0; i < itemCount; ++i) {
2981 QString itemText = QLatin1String("item text ") + QString::number(i);
2982 comboBox.addItem(atext: itemText);
2983 }
2984
2985 for (int i = 0; i < itemCount; ++i) {
2986 QString itemText = QLatin1String("item text ") + QString::number(i);
2987 QCOMPARE(comboBox.itemText(i), itemText);
2988 QCOMPARE(comboBox.itemData(i, Qt::DisplayRole).toString(), itemText);
2989 QCOMPARE(comboBox.itemData(i, Qt::EditRole).toString(), itemText);
2990
2991 comboBox.setCurrentIndex(i);
2992 QCOMPARE(comboBox.currentIndex(), i);
2993 QCOMPARE(comboBox.currentText(), itemText);
2994 QCOMPARE(comboBox.currentData(Qt::DisplayRole).toString(), itemText);
2995 QCOMPARE(comboBox.currentData(Qt::EditRole).toString(), itemText);
2996 }
2997
2998 for (int i = 0; i < itemCount; ++i) // now change by using setItemText
2999 comboBox.setItemText(index: i, text: QLatin1String("setItemText ") + QString::number(i));
3000
3001 for (int i = 0; i < itemCount; ++i) {
3002 QString itemText = QLatin1String("setItemText ") + QString::number(i);
3003 QCOMPARE(comboBox.itemText(i), itemText);
3004 QCOMPARE(comboBox.itemData(i, Qt::DisplayRole).toString(), itemText);
3005 QCOMPARE(comboBox.itemData(i, Qt::EditRole).toString(), itemText);
3006
3007 comboBox.setCurrentIndex(i);
3008 QCOMPARE(comboBox.currentIndex(), i);
3009 QCOMPARE(comboBox.currentText(), itemText);
3010 QCOMPARE(comboBox.currentData(Qt::DisplayRole).toString(), itemText);
3011 QCOMPARE(comboBox.currentData(Qt::EditRole).toString(), itemText);
3012 }
3013
3014 for (int i = 0; i < itemCount; ++i) {
3015 // now change by changing the DisplayRole's data
3016 QString itemText = QLatin1String("setItemData(DisplayRole) ") + QString::number(i);
3017 comboBox.setItemData(index: i, value: QVariant(itemText), role: Qt::DisplayRole);
3018 }
3019
3020 for (int i = 0; i < itemCount; ++i) {
3021 QString itemText = QLatin1String("setItemData(DisplayRole) ") + QString::number(i);
3022 QCOMPARE(comboBox.itemText(i), itemText);
3023 QCOMPARE(comboBox.itemData(i, Qt::DisplayRole).toString(), itemText);
3024 QCOMPARE(comboBox.itemData(i, Qt::EditRole).toString(), itemText);
3025
3026 comboBox.setCurrentIndex(i);
3027 QCOMPARE(comboBox.currentIndex(), i);
3028 QCOMPARE(comboBox.currentText(), itemText);
3029 QCOMPARE(comboBox.currentData(Qt::DisplayRole).toString(), itemText);
3030 QCOMPARE(comboBox.currentData(Qt::EditRole).toString(), itemText);
3031 }
3032
3033 for (int i = 0; i < itemCount; ++i) {
3034 // now change by changing the EditRole's data
3035 QString itemText = QLatin1String("setItemData(EditRole) ") + QString::number(i);
3036 comboBox.setItemData(index: i, value: QVariant(itemText), role: Qt::EditRole);
3037 }
3038
3039 for (int i = 0; i < itemCount; ++i) {
3040 QString itemText = QLatin1String("setItemData(EditRole) ") + QString::number(i);
3041 QCOMPARE(comboBox.itemText(i), itemText);
3042 QCOMPARE(comboBox.itemData(i, Qt::DisplayRole).toString(), itemText);
3043 QCOMPARE(comboBox.itemData(i, Qt::EditRole).toString(), itemText);
3044
3045 comboBox.setCurrentIndex(i);
3046 QCOMPARE(comboBox.currentIndex(), i);
3047 QCOMPARE(comboBox.currentText(), itemText);
3048 QCOMPARE(comboBox.currentData(Qt::DisplayRole).toString(), itemText);
3049 QCOMPARE(comboBox.currentData(Qt::EditRole).toString(), itemText);
3050 }
3051
3052 comboBox.clear();
3053
3054
3055 // set additional user data in the addItem call
3056 for (int i = 0; i < itemCount; ++i) {
3057 const QString iS = QString::number(i);
3058 comboBox.addItem(atext: QLatin1String("item text ") + iS, auserData: QVariant(QLatin1String("item data ") + iS));
3059 }
3060
3061 for (int i = 0; i < itemCount; ++i) {
3062 const QString iS = QString::number(i);
3063 QString itemText = QLatin1String("item text ") + iS;
3064 QString itemDataText = QLatin1String("item data ") + iS;
3065 QCOMPARE(comboBox.itemData(i, Qt::DisplayRole).toString(), itemText);
3066 QCOMPARE(comboBox.itemData(i, Qt::EditRole).toString(), itemText);
3067 QCOMPARE(comboBox.itemData(i).toString(), itemDataText);
3068
3069 comboBox.setCurrentIndex(i);
3070 QCOMPARE(comboBox.currentIndex(), i);
3071 QCOMPARE(comboBox.currentData(Qt::DisplayRole).toString(), itemText);
3072 QCOMPARE(comboBox.currentData(Qt::EditRole).toString(), itemText);
3073 QCOMPARE(comboBox.currentData().toString(), itemDataText);
3074
3075 }
3076
3077 comboBox.clear();
3078
3079
3080 // additional roles, setItemData
3081 // UserRole + 0 -> string
3082 // UserRole + 1 -> double
3083 // UserRole + 2 -> icon
3084 QString qtlogoPath = QFINDTESTDATA("qtlogo.png");
3085 QIcon icon = QIcon(QPixmap(qtlogoPath));
3086 for (int i = 0; i < itemCount; ++i) {
3087 const QString iS = QString::number(i);
3088 QString itemText = QLatin1String("item text ") + iS;
3089 QString itemDataText = QLatin1String("item data ") + iS;
3090 double d = i;
3091 comboBox.addItem(atext: itemText);
3092 comboBox.setItemData(index: i, value: QVariant(itemDataText), role: Qt::UserRole);
3093 comboBox.setItemData(index: i, value: QVariant(d), role: Qt::UserRole + 1);
3094 comboBox.setItemData(index: i, value: QVariant::fromValue(value: icon), role: Qt::UserRole + 2);
3095 }
3096
3097 for (int i = 0; i < itemCount; ++i) {
3098 const QString iS = QString::number(i);
3099 QString itemText = QLatin1String("item text ") + iS;
3100 QString itemDataText = QLatin1String("item data ") + iS;
3101 double d = i;
3102 QCOMPARE(comboBox.itemData(i, Qt::DisplayRole).toString(), itemText);
3103 QCOMPARE(comboBox.itemData(i, Qt::EditRole).toString(), itemText);
3104 QCOMPARE(comboBox.itemData(i, Qt::UserRole).toString(), itemDataText);
3105 QCOMPARE(comboBox.itemData(i, Qt::UserRole + 1).toDouble(), d);
3106 QCOMPARE(comboBox.itemData(i, Qt::UserRole + 2).value<QIcon>(), icon);
3107
3108 comboBox.setCurrentIndex(i);
3109 QCOMPARE(comboBox.currentData(Qt::DisplayRole).toString(), itemText);
3110 QCOMPARE(comboBox.currentData(Qt::EditRole).toString(), itemText);
3111 QCOMPARE(comboBox.currentData(Qt::UserRole).toString(), itemDataText);
3112 QCOMPARE(comboBox.currentData(Qt::UserRole + 1).toDouble(), d);
3113 QCOMPARE(comboBox.currentData(Qt::UserRole + 2).value<QIcon>(), icon);
3114 }
3115}
3116
3117void tst_QComboBox::task_QTBUG_31146_popupCompletion()
3118{
3119 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
3120 QSKIP("Wayland: This fails. Figure out why.");
3121
3122 QComboBox comboBox;
3123 comboBox.setEditable(true);
3124#if QT_DEPRECATED_SINCE(5, 13)
3125 comboBox.setAutoCompletion(true);
3126#endif
3127 comboBox.setInsertPolicy(QComboBox::NoInsert);
3128 comboBox.completer()->setCaseSensitivity(Qt::CaseInsensitive);
3129 comboBox.completer()->setCompletionMode(QCompleter::PopupCompletion);
3130
3131 comboBox.addItems(texts: QStringList() << QStringLiteral("item") << QStringLiteral("item"));
3132
3133 comboBox.show();
3134 comboBox.activateWindow();
3135 QVERIFY(QTest::qWaitForWindowActive(&comboBox));
3136
3137 QCOMPARE(comboBox.currentIndex(), 0);
3138
3139 comboBox.lineEdit()->selectAll();
3140 QTest::keyClicks(widget: comboBox.lineEdit(), sequence: "item");
3141
3142 QTest::keyClick(widget: comboBox.completer()->popup(), key: Qt::Key_Down);
3143 QTest::keyClick(widget: comboBox.completer()->popup(), key: Qt::Key_Down);
3144 QTest::keyClick(widget: comboBox.completer()->popup(), key: Qt::Key_Enter);
3145 QCOMPARE(comboBox.currentIndex(), 1);
3146
3147 comboBox.lineEdit()->selectAll();
3148 QTest::keyClicks(widget: comboBox.lineEdit(), sequence: "item");
3149
3150 QTest::keyClick(widget: comboBox.completer()->popup(), key: Qt::Key_Up);
3151 QTest::keyClick(widget: comboBox.completer()->popup(), key: Qt::Key_Up);
3152 QTest::keyClick(widget: comboBox.completer()->popup(), key: Qt::Key_Enter);
3153 QCOMPARE(comboBox.currentIndex(), 0);
3154}
3155
3156void tst_QComboBox::task_QTBUG_41288_completerChangesCurrentIndex()
3157{
3158 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
3159 QSKIP("Wayland: This fails. Figure out why.");
3160
3161 QComboBox comboBox;
3162 comboBox.setEditable(true);
3163
3164 comboBox.addItems(texts: QStringList() << QStringLiteral("111") << QStringLiteral("222"));
3165
3166 comboBox.show();
3167 comboBox.activateWindow();
3168 QVERIFY(QTest::qWaitForWindowActive(&comboBox));
3169
3170 {
3171 // change currentIndex() by keyboard
3172 comboBox.lineEdit()->selectAll();
3173 QTest::keyClicks(widget: comboBox.lineEdit(), sequence: "222");
3174 QTest::keyClick(widget: comboBox.lineEdit(), key: Qt::Key_Enter);
3175 QCOMPARE(comboBox.currentIndex(), 1);
3176
3177 QTest::keyClick(widget: &comboBox, key: Qt::Key_Up);
3178 comboBox.lineEdit()->selectAll();
3179 QTest::keyClick(widget: comboBox.lineEdit(), key: Qt::Key_Enter);
3180 QCOMPARE(comboBox.currentIndex(), 0);
3181 }
3182
3183 {
3184 // change currentIndex() programmatically
3185 comboBox.lineEdit()->selectAll();
3186 QTest::keyClicks(widget: comboBox.lineEdit(), sequence: "222");
3187 QTest::keyClick(widget: comboBox.lineEdit(), key: Qt::Key_Enter);
3188 QCOMPARE(comboBox.currentIndex(), 1);
3189
3190 comboBox.setCurrentIndex(0);
3191 comboBox.lineEdit()->selectAll();
3192 QTest::keyClick(widget: comboBox.lineEdit(), key: Qt::Key_Enter);
3193 QCOMPARE(comboBox.currentIndex(), 0);
3194 }
3195}
3196
3197namespace {
3198 struct SetReadOnly {
3199 QComboBox *cb;
3200 explicit SetReadOnly(QComboBox *cb) : cb(cb) {}
3201 void operator()() const
3202 { cb->setEditable(false); }
3203 };
3204}
3205
3206void tst_QComboBox::task_QTBUG_54191_slotOnEditTextChangedSetsComboBoxToReadOnly()
3207{
3208 QComboBox cb;
3209 cb.addItems(texts: QStringList() << "one" << "two");
3210 cb.setEditable(true);
3211 cb.setCurrentIndex(0);
3212
3213 connect(sender: &cb, signal: &QComboBox::editTextChanged,
3214 slot: SetReadOnly(&cb));
3215
3216 cb.setCurrentIndex(1);
3217 // the real test is that it didn't crash...
3218 QCOMPARE(cb.currentIndex(), 1);
3219}
3220
3221void tst_QComboBox::keyboardSelection()
3222{
3223 QComboBox comboBox;
3224 const int keyboardInterval = QApplication::keyboardInputInterval();
3225 QStringList list;
3226 list << "OA" << "OB" << "OC" << "OO" << "OP" << "PP";
3227 comboBox.addItems(texts: list);
3228
3229 // Clear any remaining keyboard input from previous tests.
3230 QTest::qWait(ms: keyboardInterval);
3231 QTest::keyClicks(widget: &comboBox, sequence: "oo", modifier: Qt::NoModifier, delay: 50);
3232 QCOMPARE(comboBox.currentText(), list.at(3));
3233
3234 QTest::qWait(ms: keyboardInterval);
3235 QTest::keyClicks(widget: &comboBox, sequence: "op", modifier: Qt::NoModifier, delay: 50);
3236 QCOMPARE(comboBox.currentText(), list.at(4));
3237
3238 QTest::keyClick(widget: &comboBox, key: Qt::Key_P, modifier: Qt::NoModifier, delay: keyboardInterval);
3239 QCOMPARE(comboBox.currentText(), list.at(5));
3240
3241 QTest::keyClick(widget: &comboBox, key: Qt::Key_O, modifier: Qt::NoModifier, delay: keyboardInterval);
3242 QCOMPARE(comboBox.currentText(), list.at(0));
3243
3244 QTest::keyClick(widget: &comboBox, key: Qt::Key_O, modifier: Qt::NoModifier, delay: keyboardInterval);
3245 QCOMPARE(comboBox.currentText(), list.at(1));
3246}
3247
3248void tst_QComboBox::updateDelegateOnEditableChange()
3249{
3250
3251 QComboBox box;
3252 box.addItem(QStringLiteral("Foo"));
3253 box.addItem(QStringLiteral("Bar"));
3254 box.setEditable(false);
3255
3256 QComboBoxPrivate *d = static_cast<QComboBoxPrivate *>(QComboBoxPrivate::get(w: &box));
3257
3258 {
3259 bool menuDelegateBefore = qobject_cast<QComboMenuDelegate *>(object: box.itemDelegate()) != 0;
3260 d->updateDelegate();
3261 bool menuDelegateAfter = qobject_cast<QComboMenuDelegate *>(object: box.itemDelegate()) != 0;
3262 QCOMPARE(menuDelegateAfter, menuDelegateBefore);
3263 }
3264
3265 box.setEditable(true);
3266
3267 {
3268 bool menuDelegateBefore = qobject_cast<QComboMenuDelegate *>(object: box.itemDelegate()) != 0;
3269 d->updateDelegate();
3270 bool menuDelegateAfter = qobject_cast<QComboMenuDelegate *>(object: box.itemDelegate()) != 0;
3271 QCOMPARE(menuDelegateAfter, menuDelegateBefore);
3272 }
3273}
3274
3275void tst_QComboBox::task_QTBUG_39088_inputMethodHints()
3276{
3277 QComboBox box;
3278 box.setEditable(true);
3279 box.setInputMethodHints(Qt::ImhNoPredictiveText);
3280 QCOMPARE(box.lineEdit()->inputMethodHints(), Qt::ImhNoPredictiveText);
3281}
3282
3283void tst_QComboBox::respectChangedOwnershipOfItemView()
3284{
3285 QComboBox box1;
3286 QComboBox box2;
3287 QTableView *v1 = new QTableView;
3288 box1.setView(v1);
3289
3290 QSignalSpy spy1(v1, SIGNAL(destroyed()));
3291 box2.setView(v1); // Ownership should now be transferred to box2
3292
3293
3294 QTableView *v2 = new QTableView(&box1);
3295 box1.setView(v2); // Here we do not expect v1 to be deleted
3296 QApplication::processEvents();
3297 QCOMPARE(spy1.count(), 0);
3298
3299 QSignalSpy spy2(v2, SIGNAL(destroyed()));
3300 box1.setView(v1);
3301 QCOMPARE(spy2.count(), 1);
3302}
3303
3304void tst_QComboBox::task_QTBUG_49831_scrollerNotActivated()
3305{
3306 QStringList modelData;
3307 for (int i = 0; i < 1000; i++)
3308 modelData << QStringLiteral("Item %1").arg(a: i);
3309 QStringListModel model(modelData);
3310
3311 QComboBox box;
3312 box.setModel(&model);
3313 box.setCurrentIndex(500);
3314 box.show();
3315 QVERIFY(QTest::qWaitForWindowExposed(&box));
3316 QTest::mouseMove(widget: &box, pos: QPoint(5, 5), delay: 100);
3317 box.showPopup();
3318 QFrame *container = box.findChild<QComboBoxPrivateContainer *>();
3319 QVERIFY(container);
3320 QVERIFY(QTest::qWaitForWindowExposed(container));
3321
3322 QList<QComboBoxPrivateScroller *> scrollers = container->findChildren<QComboBoxPrivateScroller *>();
3323 // Not all styles support scrollers. We rely only on those platforms that do to catch any regression.
3324 if (!scrollers.isEmpty()) {
3325 Q_FOREACH (QComboBoxPrivateScroller *scroller, scrollers) {
3326 if (scroller->isVisible()) {
3327 QSignalSpy doScrollSpy(scroller, SIGNAL(doScroll(int)));
3328 QTest::mouseMove(widget: scroller, pos: QPoint(5, 5), delay: 500);
3329 QTest::mouseMove(widget: scroller, pos: QPoint(6, 5), delay: 500);
3330 QTRY_VERIFY(doScrollSpy.count() > 0);
3331 }
3332 }
3333 }
3334}
3335
3336class QTBUG_56693_Model : public QStandardItemModel
3337{
3338public:
3339 QTBUG_56693_Model(QObject *parent = nullptr)
3340 : QStandardItemModel(parent)
3341 { }
3342
3343 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
3344 {
3345 if (role == Qt::FontRole) {
3346 if (index.row() < 5) {
3347 QFont font = QApplication::font();
3348 font.setItalic(true);
3349 return font;
3350 } else {
3351 return QApplication::font();
3352 }
3353 }
3354 return QStandardItemModel::data(index, role);
3355 }
3356};
3357
3358class QTBUG_56693_ProxyStyle : public QProxyStyle
3359{
3360public:
3361 QTBUG_56693_ProxyStyle(QStyle *style)
3362 : QProxyStyle(style), italicItemsNo(0)
3363 {
3364
3365 }
3366
3367 void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *w = nullptr) const override
3368 {
3369 if (element == CE_MenuItem)
3370 if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(opt))
3371 if (menuItem->font.italic())
3372 italicItemsNo++;
3373
3374 baseStyle()->drawControl(element, opt, p, w);
3375 }
3376
3377 mutable int italicItemsNo;
3378};
3379
3380void tst_QComboBox::task_QTBUG_56693_itemFontFromModel()
3381{
3382 QComboBox box;
3383 if (!qobject_cast<QComboMenuDelegate *>(object: box.itemDelegate()))
3384 QSKIP("Only for combo boxes using QComboMenuDelegate");
3385
3386 QTBUG_56693_Model model;
3387 box.setModel(&model);
3388
3389 QTBUG_56693_ProxyStyle *proxyStyle = new QTBUG_56693_ProxyStyle(box.style());
3390 box.setStyle(proxyStyle);
3391 box.setFont(QApplication::font());
3392
3393 for (int i = 0; i < 10; i++)
3394 box.addItem(atext: QLatin1String("Item ") + QString::number(i));
3395
3396 box.show();
3397 QVERIFY(QTest::qWaitForWindowExposed(&box));
3398 box.showPopup();
3399 QFrame *container = box.findChild<QComboBoxPrivateContainer *>();
3400 QVERIFY(container);
3401 QVERIFY(QTest::qWaitForWindowExposed(container));
3402
3403#ifdef Q_OS_WINRT
3404 QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort);
3405#endif
3406 QCOMPARE(proxyStyle->italicItemsNo, 5);
3407
3408 box.hidePopup();
3409}
3410
3411void tst_QComboBox::inputMethodUpdate()
3412{
3413 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
3414 QSKIP("Wayland: This fails. Figure out why.");
3415
3416 TestWidget topLevel;
3417 topLevel.show();
3418 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
3419 QComboBox *testWidget = topLevel.comboBox();
3420 // make sure we have no lineedit
3421 QVERIFY(!testWidget->lineEdit());
3422 // test setEditable(true)
3423 testWidget->setEditable(true);
3424 QVERIFY(testWidget->lineEdit());
3425
3426 testWidget->activateWindow();
3427 testWidget->setFocus();
3428 QTRY_VERIFY(testWidget->hasFocus());
3429 QTRY_COMPARE(qApp->focusObject(), testWidget);
3430
3431 m_platformInputContext.m_updateCallCount = 0;
3432 {
3433 QList<QInputMethodEvent::Attribute> attributes;
3434 QInputMethodEvent event("preedit text", attributes);
3435 QApplication::sendEvent(receiver: testWidget, event: &event);
3436 }
3437 QVERIFY(m_platformInputContext.m_updateCallCount >= 1);
3438
3439 m_platformInputContext.m_updateCallCount = 0;
3440 {
3441 QList<QInputMethodEvent::Attribute> attributes;
3442 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant());
3443 QInputMethodEvent event("preedit text", attributes);
3444 QApplication::sendEvent(receiver: testWidget, event: &event);
3445 }
3446 QVERIFY(m_platformInputContext.m_updateCallCount >= 1);
3447
3448 m_platformInputContext.m_updateCallCount = 0;
3449 {
3450 QList<QInputMethodEvent::Attribute> attributes;
3451 QInputMethodEvent event("", attributes);
3452 event.setCommitString(commitString: "preedit text");
3453 QApplication::sendEvent(receiver: testWidget, event: &event);
3454 }
3455 QVERIFY(m_platformInputContext.m_updateCallCount >= 1);
3456 QCOMPARE(testWidget->lineEdit()->text(), QString("preedit text"));
3457
3458 m_platformInputContext.m_updateCallCount = 0;
3459 {
3460 QList<QInputMethodEvent::Attribute> attributes;
3461 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant());
3462 QInputMethodEvent event("", attributes);
3463 QApplication::sendEvent(receiver: testWidget, event: &event);
3464 }
3465 QVERIFY(m_platformInputContext.m_updateCallCount >= 1);
3466}
3467
3468void tst_QComboBox::task_QTBUG_52027_mapCompleterIndex()
3469{
3470 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
3471 QSKIP("Wayland: This fails. Figure out why.");
3472
3473 QStringList words;
3474 words << "" << "foobar1" << "foobar2";
3475
3476 QStringList altWords;
3477 altWords << "foobar2" << "hello" << "," << "world" << "" << "foobar0" << "foobar1";
3478
3479 QComboBox cbox;
3480 setFrameless(&cbox);
3481 cbox.setEditable(true);
3482 cbox.setInsertPolicy(QComboBox::NoInsert);
3483 cbox.addItems(texts: words);
3484
3485 QCompleter *completer = new QCompleter(altWords);
3486 completer->setCaseSensitivity(Qt::CaseInsensitive);
3487 cbox.setCompleter(completer);
3488
3489 QSignalSpy spy(&cbox, SIGNAL(activated(int)));
3490 QCOMPARE(spy.count(), 0);
3491 cbox.move(ax: 200, ay: 200);
3492 cbox.show();
3493 QApplication::setActiveWindow(&cbox);
3494 QVERIFY(QTest::qWaitForWindowActive(&cbox));
3495
3496 QTest::keyClicks(widget: &cbox, sequence: "foobar2");
3497 QApplication::processEvents();
3498 QTRY_VERIFY(completer->popup());
3499 QTest::keyClick(widget: completer->popup(), key: Qt::Key_Down);
3500 QApplication::processEvents();
3501 QTest::keyClick(widget: completer->popup(), key: Qt::Key_Return);
3502 QApplication::processEvents();
3503 QList<QVariant> arguments = spy.takeLast();
3504 QCOMPARE(arguments.at(0).toInt(), 2);
3505
3506 cbox.lineEdit()->selectAll();
3507 cbox.lineEdit()->del();
3508
3509 QSortFilterProxyModel* model = new QSortFilterProxyModel();
3510 model->setSourceModel(cbox.model());
3511 model->setFilterFixedString("foobar1");
3512 completer->setModel(model);
3513
3514 if (QGuiApplication::platformName() == "offscreen") {
3515 QWARN("Offscreen platform requires explicit activateWindow()");
3516 cbox.activateWindow();
3517 }
3518
3519 QApplication::setActiveWindow(&cbox);
3520 QVERIFY(QTest::qWaitForWindowActive(&cbox));
3521
3522 QTest::keyClicks(widget: &cbox, sequence: "foobar1");
3523 QApplication::processEvents();
3524 QTRY_VERIFY(completer->popup());
3525 QTest::keyClick(widget: completer->popup(), key: Qt::Key_Down);
3526 QApplication::processEvents();
3527 QTest::keyClick(widget: completer->popup(), key: Qt::Key_Return);
3528 QApplication::processEvents();
3529 arguments = spy.takeLast();
3530 QCOMPARE(arguments.at(0).toInt(), 1);
3531}
3532
3533void tst_QComboBox::checkMenuItemPosWhenStyleSheetIsSet()
3534{
3535 QString newCss = "QComboBox {font-size: 18pt;}";
3536 QString oldCss = qApp->styleSheet();
3537 qApp->setStyleSheet(newCss);
3538
3539 QWidget topLevel;
3540 QVBoxLayout *layout = new QVBoxLayout(&topLevel);
3541 QStackedWidget *stack = new QStackedWidget(&topLevel);
3542 layout->addWidget(stack);
3543 QWidget *container = new QWidget(&topLevel);
3544 QHBoxLayout *cLayout = new QHBoxLayout(container);
3545 QComboBox *cBox = new QComboBox;
3546
3547 QStandardItemModel *model = new QStandardItemModel(cBox);
3548 QStandardItem *item = new QStandardItem(QStringLiteral("Item1"));
3549 model->appendRow(aitem: item);
3550 item = new QStandardItem(QStringLiteral("Item2"));
3551 model->appendRow(aitem: item);
3552 item = new QStandardItem(QStringLiteral("Item3"));
3553 model->appendRow(aitem: item);
3554 item = new QStandardItem(QStringLiteral("Item4"));
3555 model->appendRow(aitem: item);
3556 cBox->setModel(model);
3557
3558 cLayout->addWidget(cBox);
3559 stack->addWidget(w: container);
3560 topLevel.show();
3561 cBox->showPopup();
3562
3563 QTRY_VERIFY(cBox->view());
3564 QTRY_VERIFY(cBox->view()->isVisible());
3565
3566 int menuHeight = cBox->view()->geometry().height();
3567 QRect menuItemRect = cBox->view()->visualRect(index: model->indexFromItem(item));
3568
3569 QCOMPARE(menuHeight, menuItemRect.y() + menuItemRect.height());
3570
3571 qApp->setStyleSheet(oldCss);
3572}
3573
3574void tst_QComboBox::checkEmbeddedLineEditWhenStyleSheetIsSet()
3575{
3576 QString newCss = "QWidget { background-color: red; color: white; }";
3577 QString oldCss = qApp->styleSheet();
3578 qApp->setStyleSheet(newCss);
3579
3580 QWidget topLevel;
3581 auto layout = new QVBoxLayout(&topLevel);
3582 topLevel.setLayout(layout);
3583 auto comboBox = new QComboBox;
3584 layout->addWidget(comboBox);
3585 topLevel.show();
3586 comboBox->setEditable(true);
3587 QApplication::setActiveWindow(&topLevel);
3588 QVERIFY(QTest::qWaitForWindowActive(&topLevel));
3589
3590 QImage grab = comboBox->grab().toImage();
3591 auto color = grab.pixelColor(pt: grab.rect().center());
3592
3593 QVERIFY(color.red() > 240);
3594 QVERIFY(color.green() < 20);
3595 QVERIFY(color.blue() < 20);
3596
3597 qApp->setStyleSheet(oldCss);
3598}
3599
3600/*!
3601 Tests that the style-based frame style propagates to the internal container
3602 widget of QComboBox when the style changes by verifying that the respective
3603 styleHint is asked for when the style changes.
3604
3605 See QTBUG-92488
3606*/
3607void tst_QComboBox::propagateStyleChanges()
3608{
3609 class FrameStyle : public QProxyStyle
3610 {
3611 public:
3612 FrameStyle(int frameStyle, QStyle *style = nullptr)
3613 : QProxyStyle(style), frameStyle(frameStyle)
3614 {}
3615
3616 int styleHint(QStyle::StyleHint hint, const QStyleOption *opt,
3617 const QWidget *widget, QStyleHintReturn *returnData) const
3618 {
3619 if (hint == QStyle::SH_ComboBox_PopupFrameStyle) {
3620 inquired = true;
3621 return frameStyle;
3622 }
3623 return QProxyStyle::styleHint(hint, option: opt, widget, returnData);
3624 }
3625
3626 int frameStyle;
3627 mutable bool inquired = false;
3628 };
3629
3630 FrameStyle framelessStyle(QFrame::NoFrame);
3631 FrameStyle frameStyle(QFrame::Plain | QFrame::Sunken);
3632
3633 QComboBox combo;
3634 // container will be created and take settings from this style
3635 combo.setStyle(&framelessStyle);
3636 QVERIFY(framelessStyle.inquired);
3637 combo.addItem(atext: QLatin1String("Open"));
3638 combo.addItem(atext: QLatin1String("Close"));
3639 // needed because of QComboBox's adjustSizeTimer not doing anything otherwise
3640 combo.setSizeAdjustPolicy(QComboBox::AdjustToContents);
3641 combo.setStyle(&frameStyle);
3642 QVERIFY(frameStyle.inquired);
3643}
3644
3645QTEST_MAIN(tst_QComboBox)
3646#include "tst_qcombobox.moc"
3647

source code of qtbase/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp