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 | |
72 | using namespace QTestPrivate; |
73 | |
74 | class tst_QComboBox : public QObject |
75 | { |
76 | Q_OBJECT |
77 | |
78 | public: |
79 | tst_QComboBox() {} |
80 | |
81 | private 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 | |
172 | private: |
173 | PlatformInputContext m_platformInputContext; |
174 | }; |
175 | |
176 | class MyAbstractItemDelegate : public QAbstractItemDelegate |
177 | { |
178 | public: |
179 | MyAbstractItemDelegate() : QAbstractItemDelegate() {}; |
180 | void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const {} |
181 | QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const { return QSize(); } |
182 | }; |
183 | |
184 | class MyAbstractItemModel: public QAbstractItemModel |
185 | { |
186 | public: |
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 | |
203 | class MyAbstractItemView : public QAbstractItemView |
204 | { |
205 | public: |
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(); } |
210 | protected: |
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 | |
219 | void tst_QComboBox::initTestCase() |
220 | { |
221 | QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod()); |
222 | inputMethodPrivate->testContext = &m_platformInputContext; |
223 | } |
224 | |
225 | void tst_QComboBox::cleanupTestCase() |
226 | { |
227 | QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod()); |
228 | inputMethodPrivate->testContext = 0; |
229 | } |
230 | |
231 | // Testing get/set functions |
232 | void 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 | |
431 | typedef QList<QVariant> VariantList; |
432 | typedef QList<QIcon> IconList; |
433 | |
434 | Q_DECLARE_METATYPE(QComboBox::InsertPolicy) |
435 | |
436 | class TestWidget : public QWidget |
437 | { |
438 | public: |
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 | |
451 | private: |
452 | QComboBox *m_comboBox; |
453 | |
454 | |
455 | }; |
456 | |
457 | void 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 | |
482 | void 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 | |
522 | void 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 | |
603 | void 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 | |
636 | void 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 | |
775 | void 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. |
814 | void 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 | |
870 | void 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 | |
975 | void 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 | |
995 | void 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 | |
1185 | void 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 | |
1268 | void 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 | |
1288 | void 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 | |
1309 | void 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 | |
1329 | void 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 | |
1356 | void 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 | |
1369 | void 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 | |
1411 | void 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 | |
1446 | void 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 | |
1468 | void 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 | |
1475 | void 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 | |
1523 | void 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 | |
1530 | void 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 | |
1580 | void 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 | |
1636 | void 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 | |
1697 | void 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 | |
1746 | void 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 | |
1762 | void 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 | |
1823 | void 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 | |
1867 | class ReturnClass : public QWidget |
1868 | { |
1869 | Q_OBJECT |
1870 | public: |
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 | |
1893 | void 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 | |
1907 | void 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 | } |
1929 | void 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 | |
1946 | typedef QList<int> IntList; |
1947 | typedef QList<Qt::Key> KeyList; |
1948 | Q_DECLARE_METATYPE(KeyList) |
1949 | |
1950 | void 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 | |
2038 | void 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 | |
2084 | void 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 | |
2108 | void 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 | |
2140 | void 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 | |
2175 | void 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 = 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 | |
2207 | void 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 | |
2244 | void 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 = 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 | |
2279 | void 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 | |
2288 | void 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 |
2310 | void 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 | |
2348 | class task166349_ComboBox : public QComboBox |
2349 | { |
2350 | Q_OBJECT |
2351 | public: |
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 | } |
2359 | public slots: |
2360 | void onCurrentIndexChanged(int index) |
2361 | { |
2362 | setEditable(index % 2 == 1); |
2363 | } |
2364 | }; |
2365 | |
2366 | void 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 |
2377 | void 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 | |
2426 | void 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 | |
2462 | void tst_QComboBox::() |
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 | |
2489 | void 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 | |
2511 | void 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 | |
2550 | void 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 | |
2576 | void 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 | |
2592 | void 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 | |
2636 | void 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 | |
2645 | void 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 | |
2661 | void 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 | |
2669 | void 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 |
2711 | void tst_QComboBox::() |
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 | |
2751 | void 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 | |
2768 | void 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 | |
2797 | void 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 | |
2847 | void 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 | |
2879 | void 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 | |
2887 | void 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 | |
2925 | void 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 | |
2945 | void 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 | |
2973 | void 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 | |
3117 | void tst_QComboBox::() |
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 | |
3156 | void 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 | |
3197 | namespace { |
3198 | struct SetReadOnly { |
3199 | QComboBox *cb; |
3200 | explicit SetReadOnly(QComboBox *cb) : cb(cb) {} |
3201 | void operator()() const |
3202 | { cb->setEditable(false); } |
3203 | }; |
3204 | } |
3205 | |
3206 | void 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 | |
3221 | void 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 | |
3248 | void 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 = qobject_cast<QComboMenuDelegate *>(object: box.itemDelegate()) != 0; |
3260 | d->updateDelegate(); |
3261 | bool = qobject_cast<QComboMenuDelegate *>(object: box.itemDelegate()) != 0; |
3262 | QCOMPARE(menuDelegateAfter, menuDelegateBefore); |
3263 | } |
3264 | |
3265 | box.setEditable(true); |
3266 | |
3267 | { |
3268 | bool = qobject_cast<QComboMenuDelegate *>(object: box.itemDelegate()) != 0; |
3269 | d->updateDelegate(); |
3270 | bool = qobject_cast<QComboMenuDelegate *>(object: box.itemDelegate()) != 0; |
3271 | QCOMPARE(menuDelegateAfter, menuDelegateBefore); |
3272 | } |
3273 | } |
3274 | |
3275 | void 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 | |
3283 | void 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 | |
3304 | void 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 | |
3336 | class QTBUG_56693_Model : public QStandardItemModel |
3337 | { |
3338 | public: |
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 | |
3358 | class QTBUG_56693_ProxyStyle : public QProxyStyle |
3359 | { |
3360 | public: |
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 * = 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 | |
3380 | void 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 | |
3411 | void 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 | |
3468 | void 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 | |
3533 | void tst_QComboBox::() |
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 = cBox->view()->geometry().height(); |
3567 | QRect = cBox->view()->visualRect(index: model->indexFromItem(item)); |
3568 | |
3569 | QCOMPARE(menuHeight, menuItemRect.y() + menuItemRect.height()); |
3570 | |
3571 | qApp->setStyleSheet(oldCss); |
3572 | } |
3573 | |
3574 | void 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 | */ |
3607 | void 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 | |
3645 | QTEST_MAIN(tst_QComboBox) |
3646 | #include "tst_qcombobox.moc" |
3647 | |