| 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 | |