1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #include <QtTest/QtTest>
45 #include <qabstractitemview.h>
46 #include <qstandarditemmodel.h>
47 #include <qapplication.h>
48 #include <qdatetimeedit.h>
50 #include <qlistview.h>
51 #include <qtableview.h>
52 #include <qtreeview.h>
53 #include <qheaderview.h>
54 #include <qitemeditorfactory.h>
55 #include <qlineedit.h>
56 #include <qvalidator.h>
57 #include <qtablewidget.h>
58 #include <qtreewidget.h>
60 #include <QItemDelegate>
62 #include <QAbstractItemDelegate>
64 #include <QPlainTextEdit>
67 Q_DECLARE_METATYPE(QAbstractItemDelegate::EndEditHint)
69 #if defined (Q_OS_WIN) && !defined(Q_OS_WINCE)
71 #define Q_CHECK_PAINTEVENTS \
72 if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \
73 QSKIP("The widgets don't get the paint events");
75 #define Q_CHECK_PAINTEVENTS
78 //Begin of class definitions
80 class TestItemDelegate : public QItemDelegate
83 TestItemDelegate(QObject *parent = 0) : QItemDelegate(parent) {}
84 ~TestItemDelegate() {}
86 void drawDisplay(QPainter *painter,
87 const QStyleOptionViewItem &option,
88 const QRect &rect, const QString &text) const
91 displayFont = option.font;
92 QItemDelegate::drawDisplay(painter, option, rect, text);
95 void drawDecoration(QPainter *painter,
96 const QStyleOptionViewItem &option,
97 const QRect &rect, const QPixmap &pixmap) const
99 decorationPixmap = pixmap;
100 decorationRect = rect;
101 QItemDelegate::drawDecoration(painter, option, rect, pixmap);
105 inline QRect textRectangle(QPainter * painter, const QRect &rect,
106 const QFont &font, const QString &text) const
108 return QItemDelegate::textRectangle(painter, rect, font, text);
111 inline void doLayout(const QStyleOptionViewItem &option,
112 QRect *checkRect, QRect *pixmapRect,
113 QRect *textRect, bool hint) const
115 QItemDelegate::doLayout(option, checkRect, pixmapRect, textRect, hint);
118 inline QRect rect(const QStyleOptionViewItem &option,
119 const QModelIndex &index, int role) const
121 return QItemDelegate::rect(option, index, role);
124 inline bool eventFilter(QObject *object, QEvent *event)
126 return QItemDelegate::eventFilter(object, event);
129 inline bool editorEvent(QEvent *event,
130 QAbstractItemModel *model,
131 const QStyleOptionViewItem &option,
132 const QModelIndex &index)
134 return QItemDelegate::editorEvent(event, model, option, index);
137 // stored values for testing
138 mutable QString displayText;
139 mutable QFont displayFont;
140 mutable QPixmap decorationPixmap;
141 mutable QRect decorationRect;
144 class TestItemModel : public QAbstractTableModel
156 TestItemModel(const QSize &size) : size(size) {}
160 int rowCount(const QModelIndex &parent) const
166 int columnCount(const QModelIndex &parent) const
172 QVariant data(const QModelIndex& index, int role) const
175 static QPixmap pixmap(size);
176 static QImage image(size, QImage::Format_Mono);
177 static QIcon icon(pixmap);
178 static QColor color(Qt::green);
181 case PixmapTestRole: return pixmap;
182 case ImageTestRole: return image;
183 case IconTestRole: return icon;
184 case ColorTestRole: return color;
185 case DoubleTestRole: return 10.00000001;
196 class tst_QItemDelegate : public QObject
202 virtual ~tst_QItemDelegate();
206 void cleanupTestCase();
210 void textRectangle_data();
211 void textRectangle();
212 void sizeHint_data();
214 void editorKeyPress_data();
215 void editorKeyPress();
216 void doubleEditorNegativeInput();
219 void doLayout_data();
224 void dateTimeEditor_data();
225 void dateTimeEditor();
226 void dateAndTimeEditorTest2();
227 void decoration_data();
229 void editorEvent_data();
231 void enterKey_data();
235 void task257859_finalizeEdit();
236 void QTBUG4435_keepSelectionOnCheck();
240 //End of class definitions
242 // Testing get/set functions
243 void tst_QItemDelegate::getSetCheck()
247 // QItemEditorFactory * QItemDelegate::itemEditorFactory()
248 // void QItemDelegate::setItemEditorFactory(QItemEditorFactory *)
249 QItemEditorFactory *var1 = new QItemEditorFactory;
250 obj1.setItemEditorFactory(var1);
251 QCOMPARE(var1, obj1.itemEditorFactory());
252 obj1.setItemEditorFactory((QItemEditorFactory *)0);
253 QCOMPARE((QItemEditorFactory *)0, obj1.itemEditorFactory());
256 QCOMPARE(obj1.hasClipping(), true);
257 obj1.setClipping(false);
258 QCOMPARE(obj1.hasClipping(), false);
259 obj1.setClipping(true);
260 QCOMPARE(obj1.hasClipping(), true);
263 tst_QItemDelegate::tst_QItemDelegate()
267 tst_QItemDelegate::~tst_QItemDelegate()
271 void tst_QItemDelegate::initTestCase()
275 void tst_QItemDelegate::cleanupTestCase()
279 void tst_QItemDelegate::init()
283 void tst_QItemDelegate::cleanup()
287 void tst_QItemDelegate::textRectangle_data()
290 QFontMetrics fontMetrics(font);
291 int pm = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin);
292 int margins = 2 * (pm + 1); // margin on each side of the text
293 int height = fontMetrics.height();
295 QTest::addColumn<QString>("text");
296 QTest::addColumn<QRect>("rect");
297 QTest::addColumn<QRect>("expected");
299 QTest::newRow("empty") << QString()
301 << QRect(0, 0, margins, height);
304 void tst_QItemDelegate::textRectangle()
306 QFETCH(QString, text);
308 QFETCH(QRect, expected);
311 TestItemDelegate delegate;
312 QRect result = delegate.textRectangle(0, rect, font, text);
314 QCOMPARE(result, expected);
317 void tst_QItemDelegate::sizeHint_data()
319 QTest::addColumn<QSize>("expected");
322 QFontMetrics fontMetrics(font);
323 //int m = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
324 QTest::newRow("empty")
325 << QSize(0, fontMetrics.height());
329 void tst_QItemDelegate::sizeHint()
331 QFETCH(QSize, expected);
334 QStyleOptionViewItem option;
336 TestItemDelegate delegate;
337 QSize result = delegate.sizeHint(option, index);
338 QCOMPARE(result, expected);
341 void tst_QItemDelegate::editorKeyPress_data()
343 QTest::addColumn<QString>("initial");
344 QTest::addColumn<QString>("expected");
346 QTest::newRow("foo bar")
351 void tst_QItemDelegate::editorKeyPress()
353 QFETCH(QString, initial);
354 QFETCH(QString, expected);
356 QStandardItemModel model;
357 model.appendRow(new QStandardItem(initial));
360 view.setModel(&model);
363 QModelIndex index = model.index(0, 0);
364 view.setCurrentIndex(index); // the editor will only selectAll on the current index
367 QList<QLineEdit*> lineEditors = qFindChildren<QLineEdit *>(view.viewport());
368 QCOMPARE(lineEditors.count(), 1);
370 QLineEdit *editor = lineEditors.at(0);
371 QCOMPARE(editor->selectedText(), initial);
373 QTest::keyClicks(editor, expected);
374 QTest::keyClick(editor, Qt::Key_Enter);
375 QApplication::processEvents();
377 QCOMPARE(index.data().toString(), expected);
380 void tst_QItemDelegate::doubleEditorNegativeInput()
382 QStandardItemModel model;
384 QStandardItem *item = new QStandardItem;
385 item->setData(10.0, Qt::DisplayRole);
386 model.appendRow(item);
389 view.setModel(&model);
392 QModelIndex index = model.index(0, 0);
393 view.setCurrentIndex(index); // the editor will only selectAll on the current index
396 QList<QDoubleSpinBox*> editors = qFindChildren<QDoubleSpinBox *>(view.viewport());
397 QCOMPARE(editors.count(), 1);
399 QDoubleSpinBox *editor = editors.at(0);
400 QCOMPARE(editor->value(), double(10));
402 QTest::keyClick(editor, Qt::Key_Minus);
403 QTest::keyClick(editor, Qt::Key_1);
404 QTest::keyClick(editor, Qt::Key_0);
405 QTest::keyClick(editor, Qt::Key_Comma); //support both , and . locales
406 QTest::keyClick(editor, Qt::Key_Period);
407 QTest::keyClick(editor, Qt::Key_0);
408 QTest::keyClick(editor, Qt::Key_Enter);
409 QApplication::processEvents();
411 QCOMPARE(index.data().toString(), QString("-10"));
414 void tst_QItemDelegate::font_data()
416 QTest::addColumn<QString>("itemText");
417 QTest::addColumn<QString>("properties");
418 QTest::addColumn<QFont>("itemFont");
419 QTest::addColumn<QFont>("viewFont");
422 itemFont.setItalic(true);
425 QTest::newRow("foo italic")
431 itemFont.setItalic(true);
433 QTest::newRow("foo bold")
439 itemFont.setFamily(itemFont.defaultFamily());
441 QTest::newRow("foo family")
448 void tst_QItemDelegate::font()
452 QFETCH(QString, itemText);
453 QFETCH(QString, properties);
454 QFETCH(QFont, itemFont);
455 QFETCH(QFont, viewFont);
457 QTableWidget table(1, 1);
458 table.setFont(viewFont);
460 TestItemDelegate *delegate = new TestItemDelegate(&table);
461 table.setItemDelegate(delegate);
463 QVERIFY(QTest::qWaitForWindowExposed(&table));
465 QTableWidgetItem *item = new QTableWidgetItem;
466 item->setText(itemText);
467 item->setFont(itemFont);
468 table.setItem(0, 0, item);
470 QApplication::processEvents();
472 QApplication::sendPostedEvents(); //glib workaround
475 QTRY_COMPARE(delegate->displayText, item->text());
476 if (properties.contains("italic")) {
477 QCOMPARE(delegate->displayFont.italic(), item->font().italic());
479 if (properties.contains("bold")){
480 QCOMPARE(delegate->displayFont.bold(), item->font().bold());
482 if (properties.contains("family")){
483 QCOMPARE(delegate->displayFont.family(), item->font().family());
487 //Testing the different QRect created by the doLayout function.
488 //Tests are made with different values for the QStyleOptionViewItem properties:
489 //decorationPosition and position.
491 void tst_QItemDelegate::doLayout_data()
493 QTest::addColumn<int>("position");
494 QTest::addColumn<int>("direction");
495 QTest::addColumn<bool>("hint");
496 QTest::addColumn<QRect>("itemRect");
497 QTest::addColumn<QRect>("checkRect");
498 QTest::addColumn<QRect>("pixmapRect");
499 QTest::addColumn<QRect>("textRect");
500 QTest::addColumn<QRect>("expectedCheckRect");
501 QTest::addColumn<QRect>("expectedPixmapRect");
502 QTest::addColumn<QRect>("expectedTextRect");
504 int m = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
510 QTest::newRow("top, left to right, hint")
511 << (int)QStyleOptionViewItem::Top
512 << (int)Qt::LeftToRight
514 << QRect(0, 0, 400, 400)
515 << QRect(0, 0, 50, 50)
516 << QRect(0, 0, 1000, 1000)
517 << QRect(0, 0, 400, 400)
518 << QRect(m, 0, 50 + 2*m, 1000)
519 << QRect(50 + 2*m, 0, 1000 + 2*m, 1000 + m)
520 << QRect(50 + 2*m, 1000 + m, 1000 + 2*m, 400);
522 QTest::newRow("top, left to right, limited")
523 << (int)QStyleOptionViewItem::Top
524 << (int)Qt::LeftToRight
526 << QRect(0, 0, 400, 400)
527 << QRect(0, 0, 50, 50)
528 << QRect(0, 0, 1000, 1000)
529 << QRect(0, 0, 400, 400)
530 << QRect(m, (400/2) - (50/2), 50, 50)
531 << QRect(50 + 2*m, 0, 1000, 1000)
532 << QRect(50 + 2*m, 1000 + m, 400 - (50 + 2*m), 400 - 1000 - m);
534 QTest::newRow("top, right to left, hint")
535 << (int)QStyleOptionViewItem::Top
536 << (int)Qt::RightToLeft
538 << QRect(0, 0, 400, 400)
539 << QRect(0, 0, 50, 50)
540 << QRect(0, 0, 1000, 1000)
541 << QRect(0, 0, 400, 400)
542 << QRect(1000 + 2 * m, 0, 50 + 2 * m, 1000)
543 << QRect(0, 0, 1000 + 2 * m, 1000 + m)
544 << QRect(0, 1000 + m, 1000 + 2 * m, 400);
546 QTest::newRow("bottom, left to right, hint")
547 << (int)QStyleOptionViewItem::Bottom
548 << (int)Qt::LeftToRight
550 << QRect(0, 0, 400, 400)
551 << QRect(0, 0, 50, 50)
552 << QRect(0, 0, 1000, 1000)
553 << QRect(0, 0, 400, 400)
554 << QRect(m, 0, 50 + 2 * m, 1000)
555 << QRect(50 + 2 * m, 400 + m, 1000 + 2 * m, 1000)
556 << QRect(50 + 2 * m, 0, 1000 + 2 * m, 400 + m);
558 QTest::newRow("bottom, right to left, hint")
559 << (int)QStyleOptionViewItem::Bottom
560 << (int)Qt::RightToLeft
562 << QRect(0, 0, 400, 400)
563 << QRect(0, 0, 50, 50)
564 << QRect(0, 0, 1000, 1000)
565 << QRect(0, 0, 400, 400)
566 << QRect(1000 + 2 * m, 0, 50 + 2 * m, 1000)
567 << QRect(0, 400 + m, 1000 + 2 * m, 1000)
568 << QRect(0, 0, 1000 + 2 * m, 400 + m);
570 QTest::newRow("left, left to right, hint")
571 << (int)QStyleOptionViewItem::Left
572 << (int)Qt::LeftToRight
574 << QRect(0, 0, 400, 400)
575 << QRect(0, 0, 50, 50)
576 << QRect(0, 0, 1000, 1000)
577 << QRect(0, 0, 400, 400)
578 << QRect(m, 0, 50 + 2 * m, 1000)
579 << QRect(50 + 2 * m, 0, 1000 + 2 * m, 1000)
580 << QRect(1050 + 4 * m, 0, 400 + 2 * m, 1000);
582 QTest::newRow("left, right to left, hint")
583 << (int)QStyleOptionViewItem::Left
584 << (int)Qt::RightToLeft
586 << QRect(0, 0, 400, 400)
587 << QRect(0, 0, 50, 50)
588 << QRect(0, 0, 1000, 1000)
589 << QRect(0, 0, 400, 400)
590 << QRect(1400 + 4 * m, 0, 50 + 2 * m, 1000)
591 << QRect(400 + 2 * m, 0, 1000 + 2 * m, 1000)
592 << QRect(0, 0, 400 + 2 * m, 1000);
594 QTest::newRow("right, left to right, hint")
595 << (int)QStyleOptionViewItem::Right
596 << (int)Qt::LeftToRight
598 << QRect(0, 0, 400, 400)
599 << QRect(0, 0, 50, 50)
600 << QRect(0, 0, 1000, 1000)
601 << QRect(0, 0, 400, 400)
602 << QRect(m, 0, 50 + 2 * m, 1000)
603 << QRect(450 + 4 * m, 0, 1000 + 2 * m, 1000)
604 << QRect(50 + 2 * m, 0, 400 + 2 * m, 1000);
606 QTest::newRow("right, right to left, hint")
607 << (int)QStyleOptionViewItem::Right
608 << (int)Qt::RightToLeft
610 << QRect(0, 0, 400, 400)
611 << QRect(0, 0, 50, 50)
612 << QRect(0, 0, 1000, 1000)
613 << QRect(0, 0, 400, 400)
614 << QRect(1400 + 4 * m, 0, 50 + 2 * m, 1000)
615 << QRect(0, 0, 1000 + 2 * m, 1000)
616 << QRect(1000 + 2 * m, 0, 400 + 2 * m, 1000);
619 void tst_QItemDelegate::doLayout()
621 QFETCH(int, position);
622 QFETCH(int, direction);
624 QFETCH(QRect, itemRect);
625 QFETCH(QRect, checkRect);
626 QFETCH(QRect, pixmapRect);
627 QFETCH(QRect, textRect);
628 QFETCH(QRect, expectedCheckRect);
629 QFETCH(QRect, expectedPixmapRect);
630 QFETCH(QRect, expectedTextRect);
632 TestItemDelegate delegate;
633 QStyleOptionViewItem option;
635 option.rect = itemRect;
636 option.decorationPosition = (QStyleOptionViewItem::Position)position;
637 option.direction = (Qt::LayoutDirection)direction;
639 delegate.doLayout(option, &checkRect, &pixmapRect, &textRect, hint);
641 QCOMPARE(checkRect, expectedCheckRect);
642 QCOMPARE(pixmapRect, expectedPixmapRect);
643 QCOMPARE(textRect, expectedTextRect);
646 void tst_QItemDelegate::rect_data()
648 QTest::addColumn<int>("role");
649 QTest::addColumn<QSize>("size");
650 QTest::addColumn<QRect>("expected");
652 QTest::newRow("pixmap")
653 << (int)TestItemModel::PixmapTestRole
655 << QRect(0, 0, 200, 300);
657 QTest::newRow("image")
658 << (int)TestItemModel::ImageTestRole
660 << QRect(0, 0, 200, 300);
662 QTest::newRow("icon")
663 << (int)TestItemModel::IconTestRole
665 << QRect(0, 0, 200, 300);
667 QTest::newRow("color")
668 << (int)TestItemModel::ColorTestRole
670 << QRect(0, 0, 200, 300);
672 QTest::newRow("double")
673 << (int)TestItemModel::DoubleTestRole
678 void tst_QItemDelegate::rect()
682 QFETCH(QRect, expected);
684 TestItemModel model(size);
685 QStyleOptionViewItem option;
686 TestItemDelegate delegate;
687 option.decorationSize = size;
689 if (role == TestItemModel::DoubleTestRole)
690 expected = delegate.textRectangle(0, QRect(), QFont(), QLatin1String("10.00000001"));
692 QModelIndex index = model.index(0, 0);
693 QVERIFY(index.isValid());
694 QRect result = delegate.rect(option, index, role);
695 QCOMPARE(result, expected);
698 //TODO : Add a test for the keyPress event
699 //with Qt::Key_Enter and Qt::Key_Return
700 void tst_QItemDelegate::eventFilter()
702 TestItemDelegate delegate;
706 qRegisterMetaType<QAbstractItemDelegate::EndEditHint>("QAbstractItemDelegate::EndEditHint");
708 QSignalSpy commitDataSpy(&delegate, SIGNAL(commitData(QWidget *)));
709 QSignalSpy closeEditorSpy(&delegate,
710 SIGNAL(closeEditor(QWidget *,
711 QAbstractItemDelegate::EndEditHint)));
714 //For each test we send a key event and check if signals were emitted.
715 event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
716 QVERIFY(delegate.eventFilter(&widget, event));
717 QCOMPARE(closeEditorSpy.count(), 1);
718 QCOMPARE(commitDataSpy.count(), 1);
721 event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier);
722 QVERIFY(delegate.eventFilter(&widget, event));
723 QCOMPARE(closeEditorSpy.count(), 2);
724 QCOMPARE(commitDataSpy.count(), 2);
727 event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier);
728 QVERIFY(delegate.eventFilter(&widget, event));
729 QCOMPARE(closeEditorSpy.count(), 3);
730 QCOMPARE(commitDataSpy.count(), 2);
733 event = new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier);
734 QVERIFY(!delegate.eventFilter(&widget, event));
735 QCOMPARE(closeEditorSpy.count(), 3);
736 QCOMPARE(commitDataSpy.count(), 2);
740 event = new QFocusEvent(QEvent::FocusOut);
741 QVERIFY(!delegate.eventFilter(&widget, event));
742 QCOMPARE(closeEditorSpy.count(), 4);
743 QCOMPARE(commitDataSpy.count(), 3);
747 void tst_QItemDelegate::dateTimeEditor_data()
749 QTest::addColumn<QTime>("time");
750 QTest::addColumn<QDate>("date");
752 QTest::newRow("data")
754 << QDate(2006, 10, 31);
757 void tst_QItemDelegate::dateTimeEditor()
762 QTableWidgetItem *item1 = new QTableWidgetItem;
763 item1->setData(Qt::DisplayRole, time);
765 QTableWidgetItem *item2 = new QTableWidgetItem;
766 item2->setData(Qt::DisplayRole, date);
768 QTableWidgetItem *item3 = new QTableWidgetItem;
769 item3->setData(Qt::DisplayRole, QDateTime(date, time));
771 QTableWidget widget(1, 3);
772 widget.setItem(0, 0, item1);
773 widget.setItem(0, 1, item2);
774 widget.setItem(0, 2, item3);
777 widget.editItem(item1);
779 QTestEventLoop::instance().enterLoop(1);
781 QTimeEdit *timeEditor = qFindChild<QTimeEdit *>(widget.viewport());
783 QCOMPARE(timeEditor->time(), time);
784 // The data must actually be different in order for the model
786 timeEditor->setTime(time.addSecs(60));
789 qApp->setActiveWindow(&widget);
791 widget.editItem(item2);
793 QTestEventLoop::instance().enterLoop(1);
795 QDateEdit *dateEditor = qFindChild<QDateEdit *>(widget.viewport());
797 QCOMPARE(dateEditor->date(), date);
798 dateEditor->setDate(date.addDays(60));
802 widget.editItem(item3);
804 QTestEventLoop::instance().enterLoop(1);
806 QList<QDateTimeEdit *> dateTimeEditors = widget.findChildren<QDateTimeEdit *>();
807 QDateTimeEdit *dateTimeEditor = 0;
808 foreach(dateTimeEditor, dateTimeEditors)
809 if (dateTimeEditor->metaObject()->className() == QLatin1String("QDateTimeEdit"))
811 QVERIFY(dateTimeEditor);
812 QCOMPARE(dateTimeEditor->date(), date);
813 QCOMPARE(dateTimeEditor->time(), time);
814 dateTimeEditor->setTime(time.addSecs(600));
817 QVERIFY(item1->data(Qt::EditRole).userType() == QMetaType::QTime);
818 QVERIFY(item2->data(Qt::EditRole).userType() == QMetaType::QDate);
819 QVERIFY(item3->data(Qt::EditRole).userType() == QMetaType::QDateTime);
822 // A delegate where we can either enforce a certain widget or use the standard widget.
823 class ChooseEditorDelegate : public QItemDelegate
826 virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &o, const QModelIndex &i) const
829 m_editor->setParent(parent);
832 m_editor = QItemDelegate::createEditor(parent, o, i);
836 virtual void destroyEditor(QWidget *editor, const QModelIndex &i) const
837 { // This is a reimplementation of QAbstractItemDelegate::destroyEditor just set the variable m_editor to 0
838 // The only reason we do this is to avoid the not recommended direct delete of editor (destroyEditor uses deleteLater)
839 QItemDelegate::destroyEditor(editor, i); // Allow destroy
840 m_editor = 0; // but clear the variable
843 ChooseEditorDelegate(QObject *parent = 0) : QItemDelegate(parent) { }
844 void setNextOpenEditor(QWidget *w) { m_editor = w; }
845 QWidget* currentEditor() const { return m_editor; }
847 mutable QPointer<QWidget> m_editor;
850 // We could (nearly) use a normal QTableView but in order not to add many seconds to the autotest
851 // (and save a few lines) we do this
852 class FastEditItemView : public QTableView
855 QWidget* fastEdit(const QModelIndex &i) // Consider this as QAbstractItemView::edit( )
857 QWidget *v = itemDelegate()->createEditor(viewport(), viewOptions(), i);
859 itemDelegate()->setEditorData(v, i);
862 void doCloseEditor(QWidget *editor) // Consider this as QAbstractItemView::closeEditor( )
864 itemDelegate()->destroyEditor(editor, QModelIndex());
868 void tst_QItemDelegate::dateAndTimeEditorTest2()
870 // prepare createeditor
872 QStandardItemModel s;
876 ChooseEditorDelegate *d = new ChooseEditorDelegate(&w);
877 w.setItemDelegate(d);
878 const QTime time1(3, 13, 37);
879 const QDate date1(2013, 3, 7);
881 QPointer<QTimeEdit> timeEdit;
882 QPointer<QDateEdit> dateEdit;
883 QPointer<QDateTimeEdit> dateTimeEdit;
886 // a. Open time editor on empty cell + write QTime data
887 const QModelIndex i1 = s.index(0, 0);
888 timeEdit = new QTimeEdit();
889 d->setNextOpenEditor(timeEdit);
890 QCOMPARE(w.fastEdit(i1), timeEdit.data());
891 timeEdit->setTime(time1);
892 d->setModelData(timeEdit, &s, i1);
893 QCOMPARE(s.data(i1).type(), QVariant::Time); // ensure that we wrote a time variant.
894 QCOMPARE(s.data(i1).toTime(), time1); // ensure that it is the correct time.
895 w.doCloseEditor(timeEdit);
896 QVERIFY(d->currentEditor() == 0); // should happen at doCloseEditor. We only test this once.
898 // b. Test that automatic edit of a QTime value is QTimeEdit (and not QDateTimeEdit)
899 QWidget *editor = w.fastEdit(i1);
900 timeEdit = qobject_cast<QTimeEdit*>(editor);
902 QCOMPARE(timeEdit->time(), time1);
903 w.doCloseEditor(timeEdit);
905 const QTime time2(4, 14, 37);
906 const QDate date2(2014, 4, 7);
907 const QDateTime datetime1(date1, time1);
908 const QDateTime datetime2(date2, time2);
910 // c. Test that the automatic open of an QDateTime is QDateTimeEdit + value check + set check
911 s.setData(i1, datetime2);
912 editor = w.fastEdit(i1);
913 timeEdit = qobject_cast<QTimeEdit*>(editor);
914 QVERIFY(timeEdit == 0);
915 dateEdit = qobject_cast<QDateEdit*>(editor);
916 QVERIFY(dateEdit == 0);
917 dateTimeEdit = qobject_cast<QDateTimeEdit*>(editor);
918 QVERIFY(dateTimeEdit);
919 QCOMPARE(dateTimeEdit->dateTime(), datetime2);
920 dateTimeEdit->setDateTime(datetime1);
921 d->setModelData(dateTimeEdit, &s, i1);
922 QCOMPARE(s.data(i1).type(), QVariant::DateTime); // ensure that we wrote a datetime variant.
923 QCOMPARE(s.data(i1).toDateTime(), datetime1);
924 w.doCloseEditor(dateTimeEdit);
926 // d. Open date editor on empty cell + write QDate data (similar to a)
927 const QModelIndex i2 = s.index(1, 0);
928 dateEdit = new QDateEdit();
929 d->setNextOpenEditor(dateEdit);
930 QCOMPARE(w.fastEdit(i2), dateEdit.data());
931 dateEdit->setDate(date1);
932 d->setModelData(dateEdit, &s, i2);
933 QCOMPARE(s.data(i2).type(), QVariant::Date); // ensure that we wrote a time variant.
934 QCOMPARE(s.data(i2).toDate(), date1); // ensure that it is the correct date.
935 w.doCloseEditor(dateEdit);
937 // e. Test that the default editor editor (QDateEdit) on a QDate (index i2) (similar to b)
938 editor = w.fastEdit(i2);
939 dateEdit = qobject_cast<QDateEdit*>(editor);
941 QCOMPARE(dateEdit->date(), date1);
942 w.doCloseEditor(dateEdit);
945 void tst_QItemDelegate::decoration_data()
947 QTest::addColumn<int>("type");
948 QTest::addColumn<QSize>("size");
949 QTest::addColumn<QSize>("expected");
951 int pm = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
953 QTest::newRow("pixmap 30x30")
954 << (int)QVariant::Pixmap
958 QTest::newRow("image 30x30")
959 << (int)QVariant::Image
963 //The default engine scales pixmaps down if required, but never up. For WinCE we need bigger IconSize than 30
964 QTest::newRow("icon 30x30")
965 << (int)QVariant::Icon
969 QTest::newRow("color 30x30")
970 << (int)QVariant::Color
974 // This demands too much memory and potentially hangs. Feel free to uncomment
975 // for your own testing.
976 // QTest::newRow("pixmap 30x30 big")
977 // << (int)QVariant::Pixmap
978 // << QSize(1024, 1024) // Over 1M
979 // << QSize(1024, 1024);
982 void tst_QItemDelegate::decoration()
988 QFETCH(QSize, expected);
990 QTableWidget table(1, 1);
991 TestItemDelegate delegate;
992 table.setItemDelegate(&delegate);
994 QApplication::setActiveWindow(&table);
995 QVERIFY(QTest::qWaitForWindowActive(&table));
998 switch ((QVariant::Type)type) {
999 case QVariant::Pixmap: {
1005 case QVariant::Image: {
1006 QImage img(size, QImage::Format_Mono);
1007 memset(img.bits(), 0, img.byteCount());
1011 case QVariant::Icon: {
1017 case QVariant::Color:
1018 value = QColor(Qt::green);
1024 QTableWidgetItem *item = new QTableWidgetItem;
1025 item->setData(Qt::DecorationRole, value);
1026 table.setItem(0, 0, item);
1027 item->setSelected(true);
1029 QApplication::processEvents();
1031 QTRY_COMPARE(delegate.decorationRect.size(), expected);
1034 void tst_QItemDelegate::editorEvent_data()
1036 QTest::addColumn<QRect>("rect");
1037 QTest::addColumn<QString>("text");
1038 QTest::addColumn<int>("checkState");
1039 QTest::addColumn<int>("flags");
1040 QTest::addColumn<bool>("inCheck");
1041 QTest::addColumn<int>("type");
1042 QTest::addColumn<int>("button");
1043 QTest::addColumn<bool>("edited");
1044 QTest::addColumn<int>("expectedCheckState");
1046 QTest::newRow("unchecked, checkable, release")
1047 << QRect(0, 0, 20, 20)
1049 << (int)(Qt::Unchecked)
1050 << (int)(Qt::ItemIsEditable
1051 |Qt::ItemIsSelectable
1052 |Qt::ItemIsUserCheckable
1054 |Qt::ItemIsDragEnabled
1055 |Qt::ItemIsDropEnabled)
1057 << (int)(QEvent::MouseButtonRelease)
1058 << (int)(Qt::LeftButton)
1060 << (int)(Qt::Checked);
1062 QTest::newRow("checked, checkable, release")
1063 << QRect(0, 0, 20, 20)
1065 << (int)(Qt::Checked)
1066 << (int)(Qt::ItemIsEditable
1067 |Qt::ItemIsSelectable
1068 |Qt::ItemIsUserCheckable
1070 |Qt::ItemIsDragEnabled
1071 |Qt::ItemIsDropEnabled)
1073 << (int)(QEvent::MouseButtonRelease)
1074 << (int)(Qt::LeftButton)
1076 << (int)(Qt::Unchecked);
1078 QTest::newRow("unchecked, checkable, release")
1079 << QRect(0, 0, 20, 20)
1081 << (int)(Qt::Unchecked)
1082 << (int)(Qt::ItemIsEditable
1083 |Qt::ItemIsSelectable
1084 |Qt::ItemIsUserCheckable
1086 |Qt::ItemIsDragEnabled
1087 |Qt::ItemIsDropEnabled)
1089 << (int)(QEvent::MouseButtonRelease)
1090 << (int)(Qt::LeftButton)
1092 << (int)(Qt::Checked);
1094 QTest::newRow("unchecked, checkable, release, right button")
1095 << QRect(0, 0, 20, 20)
1097 << (int)(Qt::Unchecked)
1098 << (int)(Qt::ItemIsEditable
1099 |Qt::ItemIsSelectable
1100 |Qt::ItemIsUserCheckable
1102 |Qt::ItemIsDragEnabled
1103 |Qt::ItemIsDropEnabled)
1105 << (int)(QEvent::MouseButtonRelease)
1106 << (int)(Qt::RightButton)
1108 << (int)(Qt::Unchecked);
1110 QTest::newRow("unchecked, checkable, release outside")
1111 << QRect(0, 0, 20, 20)
1113 << (int)(Qt::Unchecked)
1114 << (int)(Qt::ItemIsEditable
1115 |Qt::ItemIsSelectable
1116 |Qt::ItemIsUserCheckable
1118 |Qt::ItemIsDragEnabled
1119 |Qt::ItemIsDropEnabled)
1121 << (int)(QEvent::MouseButtonRelease)
1122 << (int)(Qt::LeftButton)
1124 << (int)(Qt::Unchecked);
1126 QTest::newRow("unchecked, checkable, dblclick")
1127 << QRect(0, 0, 20, 20)
1129 << (int)(Qt::Unchecked)
1130 << (int)(Qt::ItemIsEditable
1131 |Qt::ItemIsSelectable
1132 |Qt::ItemIsUserCheckable
1134 |Qt::ItemIsDragEnabled
1135 |Qt::ItemIsDropEnabled)
1137 << (int)(QEvent::MouseButtonDblClick)
1138 << (int)(Qt::LeftButton)
1140 << (int)(Qt::Unchecked);
1143 void tst_QItemDelegate::editorEvent()
1145 QFETCH(QRect, rect);
1146 QFETCH(QString, text);
1147 QFETCH(int, checkState);
1149 QFETCH(bool, inCheck);
1151 QFETCH(int, button);
1152 QFETCH(bool, edited);
1153 QFETCH(int, expectedCheckState);
1155 QStandardItemModel model(1, 1);
1156 QModelIndex index = model.index(0, 0);
1157 QVERIFY(index.isValid());
1159 QStandardItem *item = model.itemFromIndex(index);
1160 item->setText(text);
1161 item->setCheckState((Qt::CheckState)checkState);
1162 item->setFlags((Qt::ItemFlags)flags);
1164 QStyleOptionViewItem option;
1166 option.state |= QStyle::State_Enabled;
1167 // mimic QStyledItemDelegate::initStyleOption logic
1168 option.features |= QStyleOptionViewItem::HasCheckIndicator | QStyleOptionViewItem::HasDisplay;
1169 option.checkState = Qt::CheckState(checkState);
1171 const int checkMargin = qApp->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, 0) + 1;
1172 QPoint pos = inCheck ? qApp->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &option, 0).center() + QPoint(checkMargin, 0) : QPoint(200,200);
1174 QEvent *event = new QMouseEvent((QEvent::Type)type,
1176 (Qt::MouseButton)button,
1177 (Qt::MouseButton)button,
1179 TestItemDelegate delegate;
1180 bool wasEdited = delegate.editorEvent(event, &model, option, index);
1183 QApplication::processEvents();
1185 QCOMPARE(wasEdited, edited);
1186 QCOMPARE(index.data(Qt::CheckStateRole).toInt(), expectedCheckState);
1195 Q_DECLARE_METATYPE(WidgetType);
1197 void tst_QItemDelegate::enterKey_data()
1199 QTest::addColumn<WidgetType>("widget");
1200 QTest::addColumn<int>("key");
1201 QTest::addColumn<bool>("expectedFocus");
1203 QTest::newRow("lineedit enter") << LineEdit << int(Qt::Key_Enter) << false;
1204 QTest::newRow("textedit enter") << TextEdit << int(Qt::Key_Enter) << true;
1205 QTest::newRow("plaintextedit enter") << PlainTextEdit << int(Qt::Key_Enter) << true;
1206 QTest::newRow("plaintextedit return") << PlainTextEdit << int(Qt::Key_Return) << true;
1207 QTest::newRow("plaintextedit tab") << PlainTextEdit << int(Qt::Key_Tab) << false;
1208 QTest::newRow("lineedit tab") << LineEdit << int(Qt::Key_Tab) << false;
1211 void tst_QItemDelegate::enterKey()
1213 QFETCH(WidgetType, widget);
1215 QFETCH(bool, expectedFocus);
1217 QStandardItemModel model;
1218 model.appendRow(new QStandardItem());
1221 view.setModel(&model);
1223 QApplication::setActiveWindow(&view);
1227 struct TestDelegate : public QItemDelegate
1229 WidgetType widgetType;
1230 virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const
1232 QWidget *editor = 0;
1233 switch(widgetType) {
1235 editor = new QLineEdit(parent);
1238 editor = new QTextEdit(parent);
1241 editor = new QPlainTextEdit(parent);
1244 editor->setObjectName(QString::fromLatin1("TheEditor"));
1249 delegate.widgetType = widget;
1251 view.setItemDelegate(&delegate);
1252 QModelIndex index = model.index(0, 0);
1253 view.setCurrentIndex(index); // the editor will only selectAll on the current index
1257 QList<QWidget*> lineEditors = qFindChildren<QWidget *>(view.viewport(), QString::fromLatin1("TheEditor"));
1258 QCOMPARE(lineEditors.count(), 1);
1260 QPointer<QWidget> editor = lineEditors.at(0);
1261 QCOMPARE(editor->hasFocus(), true);
1263 QTest::keyClick(editor, Qt::Key(key));
1264 QApplication::processEvents();
1266 // The line edit has already been destroyed, so avoid that case.
1267 if (widget == TextEdit || widget == PlainTextEdit) {
1268 QVERIFY(!editor.isNull());
1269 QCOMPARE(editor && editor->hasFocus(), expectedFocus);
1273 void tst_QItemDelegate::task257859_finalizeEdit()
1275 QStandardItemModel model;
1276 model.appendRow(new QStandardItem());
1279 view.setModel(&model);
1281 QApplication::setActiveWindow(&view);
1285 QModelIndex index = model.index(0, 0);
1289 QList<QLineEdit *> lineEditors = qFindChildren<QLineEdit *>(view.viewport());
1290 QCOMPARE(lineEditors.count(), 1);
1292 QPointer<QWidget> editor = lineEditors.at(0);
1293 QCOMPARE(editor->hasFocus(), true);
1296 QTimer::singleShot(500, &dialog, SLOT(close()));
1298 QTRY_VERIFY(!editor);
1301 void tst_QItemDelegate::QTBUG4435_keepSelectionOnCheck()
1303 QStandardItemModel model(3, 1);
1304 for (int i = 0; i < 3; ++i) {
1305 QStandardItem *item = new QStandardItem(QLatin1String("Item ") + QString::number(i));
1306 item->setCheckable(true);
1307 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
1308 model.setItem(i, item);
1311 view.setModel(&model);
1312 view.setItemDelegate(new TestItemDelegate);
1315 QVERIFY(QTest::qWaitForWindowExposed(&view));
1316 QStyleOptionViewItem option;
1317 option.rect = view.visualRect(model.index(0, 0));
1318 // mimic QStyledItemDelegate::initStyleOption logic
1319 option.features = QStyleOptionViewItem::HasDisplay | QStyleOptionViewItem::HasCheckIndicator;
1320 option.checkState = Qt::CheckState(model.index(0, 0).data(Qt::CheckStateRole).toInt());
1321 const int checkMargin = qApp->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, 0) + 1;
1322 QPoint pos = qApp->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &option, 0).center()
1323 + QPoint(checkMargin, 0);
1324 QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, pos);
1325 QTRY_VERIFY(view.selectionModel()->isColumnSelected(0, QModelIndex()));
1326 QCOMPARE(model.item(0)->checkState(), Qt::Checked);
1329 void tst_QItemDelegate::comboBox()
1331 QTableWidgetItem *item1 = new QTableWidgetItem;
1332 item1->setData(Qt::DisplayRole, true);
1334 QTableWidget widget(1, 1);
1335 widget.setItem(0, 0, item1);
1338 widget.editItem(item1);
1340 QTestEventLoop::instance().enterLoop(1);
1342 QComboBox *boolEditor = qFindChild<QComboBox*>(widget.viewport());
1343 QVERIFY(boolEditor);
1344 QCOMPARE(boolEditor->currentIndex(), 1); // True is selected initially.
1345 // The data must actually be different in order for the model
1347 boolEditor->setCurrentIndex(0);
1348 QCOMPARE(boolEditor->currentIndex(), 0); // Changed to false.
1350 widget.clearFocus();
1353 QVariant data = item1->data(Qt::EditRole);
1354 QCOMPARE(data.userType(), (int)QMetaType::Bool);
1355 QCOMPARE(data.toBool(), false);
1359 // ### _not_ covered:
1361 // editing with a custom editor factory
1363 // painting when editing
1364 // painting elided text
1365 // painting wrapped text
1370 // painting selected
1378 QTEST_MAIN(tst_QItemDelegate)
1379 #include "tst_qitemdelegate.moc"