qt5base-lts/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp
Stephen Kelly c3e1abad4e Add USER properties to QDateEdit and QTimeEdit.
Both classes had such components before, but there were issues with
the NOTIFY signal not being in the same class as the Q_PROPERTY.

This patch solves that problem by using a signal of a different name.

Task-number: QTBUG-15731
Change-Id: Ibc7ce4dba8a6b88c05d62a90e14d0101c5cd3082
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
Reviewed-by: Thorbjørn Lund Martsum <tmartsum@gmail.com>
2012-03-28 02:01:49 +02:00

1230 lines
35 KiB
C++

/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <qabstractitemview.h>
#include <qstandarditemmodel.h>
#include <qapplication.h>
#include <qdatetimeedit.h>
#include <qspinbox.h>
#include <qlistview.h>
#include <qtableview.h>
#include <qtreeview.h>
#include <qheaderview.h>
#include <qitemeditorfactory.h>
#include <qlineedit.h>
#include <qvalidator.h>
#include <qtablewidget.h>
#include <qtreewidget.h>
#include <QItemDelegate>
#include <QAbstractItemDelegate>
#include <QTextEdit>
#include <QPlainTextEdit>
#include <QDialog>
Q_DECLARE_METATYPE(QAbstractItemDelegate::EndEditHint)
#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE)
#include <windows.h>
#define Q_CHECK_PAINTEVENTS \
if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \
QSKIP("The widgets don't get the paint events");
#else
#define Q_CHECK_PAINTEVENTS
#endif
//Begin of class definitions
class TestItemDelegate : public QItemDelegate
{
public:
TestItemDelegate(QObject *parent = 0) : QItemDelegate(parent) {}
~TestItemDelegate() {}
void drawDisplay(QPainter *painter,
const QStyleOptionViewItem &option,
const QRect &rect, const QString &text) const
{
displayText = text;
displayFont = option.font;
QItemDelegate::drawDisplay(painter, option, rect, text);
}
void drawDecoration(QPainter *painter,
const QStyleOptionViewItem &option,
const QRect &rect, const QPixmap &pixmap) const
{
decorationPixmap = pixmap;
decorationRect = rect;
QItemDelegate::drawDecoration(painter, option, rect, pixmap);
}
inline QRect textRectangle(QPainter * painter, const QRect &rect,
const QFont &font, const QString &text) const
{
return QItemDelegate::textRectangle(painter, rect, font, text);
}
inline void doLayout(const QStyleOptionViewItem &option,
QRect *checkRect, QRect *pixmapRect,
QRect *textRect, bool hint) const
{
QItemDelegate::doLayout(option, checkRect, pixmapRect, textRect, hint);
}
inline QRect rect(const QStyleOptionViewItem &option,
const QModelIndex &index, int role) const
{
return QItemDelegate::rect(option, index, role);
}
inline bool eventFilter(QObject *object, QEvent *event)
{
return QItemDelegate::eventFilter(object, event);
}
inline bool editorEvent(QEvent *event,
QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index)
{
return QItemDelegate::editorEvent(event, model, option, index);
}
// stored values for testing
mutable QString displayText;
mutable QFont displayFont;
mutable QPixmap decorationPixmap;
mutable QRect decorationRect;
};
class TestItemModel : public QAbstractTableModel
{
public:
enum Roles {
PixmapTestRole,
ImageTestRole,
IconTestRole,
ColorTestRole,
DoubleTestRole
};
TestItemModel(const QSize &size) : size(size) {}
~TestItemModel() {}
int rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 1;
}
int columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 1;
}
QVariant data(const QModelIndex& index, int role) const
{
Q_UNUSED(index);
static QPixmap pixmap(size);
static QImage image(size, QImage::Format_Mono);
static QIcon icon(pixmap);
static QColor color(Qt::green);
switch (role) {
case PixmapTestRole: return pixmap;
case ImageTestRole: return image;
case IconTestRole: return icon;
case ColorTestRole: return color;
case DoubleTestRole: return 10.00000001;
default: break;
}
return QVariant();
}
private:
QSize size;
};
class tst_QItemDelegate : public QObject
{
Q_OBJECT
public:
tst_QItemDelegate();
virtual ~tst_QItemDelegate();
private slots:
void initTestCase();
void cleanupTestCase();
void init();
void cleanup();
void getSetCheck();
void textRectangle_data();
void textRectangle();
void sizeHint_data();
void sizeHint();
void editorKeyPress_data();
void editorKeyPress();
void doubleEditorNegativeInput();
void font_data();
void font();
void doLayout_data();
void doLayout();
void rect_data();
void rect();
void eventFilter();
void dateTimeEditor_data();
void dateTimeEditor();
void decoration_data();
void decoration();
void editorEvent_data();
void editorEvent();
void enterKey_data();
void enterKey();
void task257859_finalizeEdit();
void QTBUG4435_keepSelectionOnCheck();
};
//End of class definitions
// Testing get/set functions
void tst_QItemDelegate::getSetCheck()
{
QItemDelegate obj1;
// QItemEditorFactory * QItemDelegate::itemEditorFactory()
// void QItemDelegate::setItemEditorFactory(QItemEditorFactory *)
QItemEditorFactory *var1 = new QItemEditorFactory;
obj1.setItemEditorFactory(var1);
QCOMPARE(var1, obj1.itemEditorFactory());
obj1.setItemEditorFactory((QItemEditorFactory *)0);
QCOMPARE((QItemEditorFactory *)0, obj1.itemEditorFactory());
delete var1;
QCOMPARE(obj1.hasClipping(), true);
obj1.setClipping(false);
QCOMPARE(obj1.hasClipping(), false);
obj1.setClipping(true);
QCOMPARE(obj1.hasClipping(), true);
}
tst_QItemDelegate::tst_QItemDelegate()
{
}
tst_QItemDelegate::~tst_QItemDelegate()
{
}
void tst_QItemDelegate::initTestCase()
{
}
void tst_QItemDelegate::cleanupTestCase()
{
}
void tst_QItemDelegate::init()
{
}
void tst_QItemDelegate::cleanup()
{
}
void tst_QItemDelegate::textRectangle_data()
{
QFont font;
QFontMetrics fontMetrics(font);
int pm = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin);
int margins = 2 * (pm + 1); // margin on each side of the text
int height = fontMetrics.height();
QTest::addColumn<QString>("text");
QTest::addColumn<QRect>("rect");
QTest::addColumn<QRect>("expected");
QTest::newRow("empty") << QString()
<< QRect()
<< QRect(0, 0, margins, height);
}
void tst_QItemDelegate::textRectangle()
{
QFETCH(QString, text);
QFETCH(QRect, rect);
QFETCH(QRect, expected);
QFont font;
TestItemDelegate delegate;
QRect result = delegate.textRectangle(0, rect, font, text);
QCOMPARE(result, expected);
}
void tst_QItemDelegate::sizeHint_data()
{
QTest::addColumn<QSize>("expected");
QFont font;
QFontMetrics fontMetrics(font);
//int m = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
QTest::newRow("empty")
<< QSize(0, fontMetrics.height());
}
void tst_QItemDelegate::sizeHint()
{
QFETCH(QSize, expected);
QModelIndex index;
QStyleOptionViewItem option;
TestItemDelegate delegate;
QSize result = delegate.sizeHint(option, index);
QCOMPARE(result, expected);
}
void tst_QItemDelegate::editorKeyPress_data()
{
QTest::addColumn<QString>("initial");
QTest::addColumn<QString>("expected");
QTest::newRow("foo bar")
<< QString("foo")
<< QString("bar");
}
void tst_QItemDelegate::editorKeyPress()
{
QFETCH(QString, initial);
QFETCH(QString, expected);
QStandardItemModel model;
model.appendRow(new QStandardItem(initial));
QListView view;
view.setModel(&model);
view.show();
QModelIndex index = model.index(0, 0);
view.setCurrentIndex(index); // the editor will only selectAll on the current index
view.edit(index);
QList<QLineEdit*> lineEditors = qFindChildren<QLineEdit *>(view.viewport());
QCOMPARE(lineEditors.count(), 1);
QLineEdit *editor = lineEditors.at(0);
QCOMPARE(editor->selectedText(), initial);
QTest::keyClicks(editor, expected);
QTest::keyClick(editor, Qt::Key_Enter);
QApplication::processEvents();
QCOMPARE(index.data().toString(), expected);
}
void tst_QItemDelegate::doubleEditorNegativeInput()
{
QStandardItemModel model;
QStandardItem *item = new QStandardItem;
item->setData(10.0, Qt::DisplayRole);
model.appendRow(item);
QListView view;
view.setModel(&model);
view.show();
QModelIndex index = model.index(0, 0);
view.setCurrentIndex(index); // the editor will only selectAll on the current index
view.edit(index);
QList<QDoubleSpinBox*> editors = qFindChildren<QDoubleSpinBox *>(view.viewport());
QCOMPARE(editors.count(), 1);
QDoubleSpinBox *editor = editors.at(0);
QCOMPARE(editor->value(), double(10));
QTest::keyClick(editor, Qt::Key_Minus);
QTest::keyClick(editor, Qt::Key_1);
QTest::keyClick(editor, Qt::Key_0);
QTest::keyClick(editor, Qt::Key_Comma); //support both , and . locales
QTest::keyClick(editor, Qt::Key_Period);
QTest::keyClick(editor, Qt::Key_0);
QTest::keyClick(editor, Qt::Key_Enter);
QApplication::processEvents();
QCOMPARE(index.data().toString(), QString("-10"));
}
void tst_QItemDelegate::font_data()
{
QTest::addColumn<QString>("itemText");
QTest::addColumn<QString>("properties");
QTest::addColumn<QFont>("itemFont");
QTest::addColumn<QFont>("viewFont");
QFont itemFont;
itemFont.setItalic(true);
QFont viewFont;
QTest::newRow("foo italic")
<< QString("foo")
<< QString("italic")
<< itemFont
<< viewFont;
itemFont.setItalic(true);
QTest::newRow("foo bold")
<< QString("foo")
<< QString("bold")
<< itemFont
<< viewFont;
itemFont.setFamily(itemFont.defaultFamily());
QTest::newRow("foo family")
<< QString("foo")
<< QString("family")
<< itemFont
<< viewFont;
}
void tst_QItemDelegate::font()
{
Q_CHECK_PAINTEVENTS
QFETCH(QString, itemText);
QFETCH(QString, properties);
QFETCH(QFont, itemFont);
QFETCH(QFont, viewFont);
QTableWidget table(1, 1);
table.setFont(viewFont);
TestItemDelegate *delegate = new TestItemDelegate(&table);
table.setItemDelegate(delegate);
table.show();
#ifdef Q_WS_X11
qt_x11_wait_for_window_manager(&table);
#endif
QTableWidgetItem *item = new QTableWidgetItem;
item->setText(itemText);
item->setFont(itemFont);
table.setItem(0, 0, item);
QApplication::processEvents();
#ifdef Q_WS_QWS
QApplication::sendPostedEvents(); //glib workaround
#endif
QTRY_COMPARE(delegate->displayText, item->text());
if (properties.contains("italic")) {
QCOMPARE(delegate->displayFont.italic(), item->font().italic());
}
if (properties.contains("bold")){
QCOMPARE(delegate->displayFont.bold(), item->font().bold());
}
if (properties.contains("family")){
QCOMPARE(delegate->displayFont.family(), item->font().family());
}
}
//Testing the different QRect created by the doLayout function.
//Tests are made with different values for the QStyleOptionViewItem properties:
//decorationPosition and position.
void tst_QItemDelegate::doLayout_data()
{
QTest::addColumn<int>("position");
QTest::addColumn<int>("direction");
QTest::addColumn<bool>("hint");
QTest::addColumn<QRect>("itemRect");
QTest::addColumn<QRect>("checkRect");
QTest::addColumn<QRect>("pixmapRect");
QTest::addColumn<QRect>("textRect");
QTest::addColumn<QRect>("expectedCheckRect");
QTest::addColumn<QRect>("expectedPixmapRect");
QTest::addColumn<QRect>("expectedTextRect");
int m = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
//int item = 400;
//int check = 50;
//int pixmap = 1000;
//int text = 400;
QTest::newRow("top, left to right, hint")
<< (int)QStyleOptionViewItem::Top
<< (int)Qt::LeftToRight
<< true
<< QRect(0, 0, 400, 400)
<< QRect(0, 0, 50, 50)
<< QRect(0, 0, 1000, 1000)
<< QRect(0, 0, 400, 400)
<< QRect(m, 0, 50 + 2*m, 1000)
<< QRect(50 + 2*m, 0, 1000 + 2*m, 1000 + m)
<< QRect(50 + 2*m, 1000 + m, 1000 + 2*m, 400);
/*
QTest::newRow("top, left to right, limited")
<< (int)QStyleOptionViewItem::Top
<< (int)Qt::LeftToRight
<< false
<< QRect(0, 0, 400, 400)
<< QRect(0, 0, 50, 50)
<< QRect(0, 0, 1000, 1000)
<< QRect(0, 0, 400, 400)
<< QRect(m, (400/2) - (50/2), 50, 50)
<< QRect(50 + 2*m, 0, 1000, 1000)
<< QRect(50 + 2*m, 1000 + m, 400 - (50 + 2*m), 400 - 1000 - m);
*/
QTest::newRow("top, right to left, hint")
<< (int)QStyleOptionViewItem::Top
<< (int)Qt::RightToLeft
<< true
<< QRect(0, 0, 400, 400)
<< QRect(0, 0, 50, 50)
<< QRect(0, 0, 1000, 1000)
<< QRect(0, 0, 400, 400)
<< QRect(1000 + 2 * m, 0, 50 + 2 * m, 1000)
<< QRect(0, 0, 1000 + 2 * m, 1000 + m)
<< QRect(0, 1000 + m, 1000 + 2 * m, 400);
QTest::newRow("bottom, left to right, hint")
<< (int)QStyleOptionViewItem::Bottom
<< (int)Qt::LeftToRight
<< true
<< QRect(0, 0, 400, 400)
<< QRect(0, 0, 50, 50)
<< QRect(0, 0, 1000, 1000)
<< QRect(0, 0, 400, 400)
<< QRect(m, 0, 50 + 2 * m, 1000)
<< QRect(50 + 2 * m, 400 + m, 1000 + 2 * m, 1000)
<< QRect(50 + 2 * m, 0, 1000 + 2 * m, 400 + m);
QTest::newRow("bottom, right to left, hint")
<< (int)QStyleOptionViewItem::Bottom
<< (int)Qt::RightToLeft
<< true
<< QRect(0, 0, 400, 400)
<< QRect(0, 0, 50, 50)
<< QRect(0, 0, 1000, 1000)
<< QRect(0, 0, 400, 400)
<< QRect(1000 + 2 * m, 0, 50 + 2 * m, 1000)
<< QRect(0, 400 + m, 1000 + 2 * m, 1000)
<< QRect(0, 0, 1000 + 2 * m, 400 + m);
QTest::newRow("left, left to right, hint")
<< (int)QStyleOptionViewItem::Left
<< (int)Qt::LeftToRight
<< true
<< QRect(0, 0, 400, 400)
<< QRect(0, 0, 50, 50)
<< QRect(0, 0, 1000, 1000)
<< QRect(0, 0, 400, 400)
<< QRect(m, 0, 50 + 2 * m, 1000)
<< QRect(50 + 2 * m, 0, 1000 + 2 * m, 1000)
<< QRect(1050 + 4 * m, 0, 400 + 2 * m, 1000);
QTest::newRow("left, right to left, hint")
<< (int)QStyleOptionViewItem::Left
<< (int)Qt::RightToLeft
<< true
<< QRect(0, 0, 400, 400)
<< QRect(0, 0, 50, 50)
<< QRect(0, 0, 1000, 1000)
<< QRect(0, 0, 400, 400)
<< QRect(1400 + 4 * m, 0, 50 + 2 * m, 1000)
<< QRect(400 + 2 * m, 0, 1000 + 2 * m, 1000)
<< QRect(0, 0, 400 + 2 * m, 1000);
QTest::newRow("right, left to right, hint")
<< (int)QStyleOptionViewItem::Right
<< (int)Qt::LeftToRight
<< true
<< QRect(0, 0, 400, 400)
<< QRect(0, 0, 50, 50)
<< QRect(0, 0, 1000, 1000)
<< QRect(0, 0, 400, 400)
<< QRect(m, 0, 50 + 2 * m, 1000)
<< QRect(450 + 4 * m, 0, 1000 + 2 * m, 1000)
<< QRect(50 + 2 * m, 0, 400 + 2 * m, 1000);
QTest::newRow("right, right to left, hint")
<< (int)QStyleOptionViewItem::Right
<< (int)Qt::RightToLeft
<< true
<< QRect(0, 0, 400, 400)
<< QRect(0, 0, 50, 50)
<< QRect(0, 0, 1000, 1000)
<< QRect(0, 0, 400, 400)
<< QRect(1400 + 4 * m, 0, 50 + 2 * m, 1000)
<< QRect(0, 0, 1000 + 2 * m, 1000)
<< QRect(1000 + 2 * m, 0, 400 + 2 * m, 1000);
}
void tst_QItemDelegate::doLayout()
{
QFETCH(int, position);
QFETCH(int, direction);
QFETCH(bool, hint);
QFETCH(QRect, itemRect);
QFETCH(QRect, checkRect);
QFETCH(QRect, pixmapRect);
QFETCH(QRect, textRect);
QFETCH(QRect, expectedCheckRect);
QFETCH(QRect, expectedPixmapRect);
QFETCH(QRect, expectedTextRect);
TestItemDelegate delegate;
QStyleOptionViewItem option;
option.rect = itemRect;
option.decorationPosition = (QStyleOptionViewItem::Position)position;
option.direction = (Qt::LayoutDirection)direction;
delegate.doLayout(option, &checkRect, &pixmapRect, &textRect, hint);
QCOMPARE(checkRect, expectedCheckRect);
QCOMPARE(pixmapRect, expectedPixmapRect);
QCOMPARE(textRect, expectedTextRect);
}
void tst_QItemDelegate::rect_data()
{
QTest::addColumn<int>("role");
QTest::addColumn<QSize>("size");
QTest::addColumn<QRect>("expected");
QTest::newRow("pixmap")
<< (int)TestItemModel::PixmapTestRole
<< QSize(200, 300)
<< QRect(0, 0, 200, 300);
QTest::newRow("image")
<< (int)TestItemModel::ImageTestRole
<< QSize(200, 300)
<< QRect(0, 0, 200, 300);
QTest::newRow("icon")
<< (int)TestItemModel::IconTestRole
<< QSize(200, 300)
<< QRect(0, 0, 200, 300);
QTest::newRow("color")
<< (int)TestItemModel::ColorTestRole
<< QSize(200, 300)
<< QRect(0, 0, 200, 300);
QTest::newRow("double")
<< (int)TestItemModel::DoubleTestRole
<< QSize()
<< QRect();
}
void tst_QItemDelegate::rect()
{
QFETCH(int, role);
QFETCH(QSize, size);
QFETCH(QRect, expected);
TestItemModel model(size);
QStyleOptionViewItem option;
TestItemDelegate delegate;
option.decorationSize = size;
if (role == TestItemModel::DoubleTestRole)
expected = delegate.textRectangle(0, QRect(), QFont(), QLatin1String("10.00000001"));
QModelIndex index = model.index(0, 0);
QVERIFY(index.isValid());
QRect result = delegate.rect(option, index, role);
QCOMPARE(result, expected);
}
//TODO : Add a test for the keyPress event
//with Qt::Key_Enter and Qt::Key_Return
void tst_QItemDelegate::eventFilter()
{
TestItemDelegate delegate;
QWidget widget;
QEvent *event;
qRegisterMetaType<QAbstractItemDelegate::EndEditHint>("QAbstractItemDelegate::EndEditHint");
QSignalSpy commitDataSpy(&delegate, SIGNAL(commitData(QWidget *)));
QSignalSpy closeEditorSpy(&delegate,
SIGNAL(closeEditor(QWidget *,
QAbstractItemDelegate::EndEditHint)));
//Subtest KeyPress
//For each test we send a key event and check if signals were emitted.
event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
QVERIFY(delegate.eventFilter(&widget, event));
QCOMPARE(closeEditorSpy.count(), 1);
QCOMPARE(commitDataSpy.count(), 1);
delete event;
event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier);
QVERIFY(delegate.eventFilter(&widget, event));
QCOMPARE(closeEditorSpy.count(), 2);
QCOMPARE(commitDataSpy.count(), 2);
delete event;
event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier);
QVERIFY(delegate.eventFilter(&widget, event));
QCOMPARE(closeEditorSpy.count(), 3);
QCOMPARE(commitDataSpy.count(), 2);
delete event;
event = new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier);
QVERIFY(!delegate.eventFilter(&widget, event));
QCOMPARE(closeEditorSpy.count(), 3);
QCOMPARE(commitDataSpy.count(), 2);
delete event;
//Subtest focusEvent
event = new QFocusEvent(QEvent::FocusOut);
QVERIFY(!delegate.eventFilter(&widget, event));
QCOMPARE(closeEditorSpy.count(), 4);
QCOMPARE(commitDataSpy.count(), 3);
delete event;
}
void tst_QItemDelegate::dateTimeEditor_data()
{
QTest::addColumn<QTime>("time");
QTest::addColumn<QDate>("date");
QTest::newRow("data")
<< QTime(7, 16, 34)
<< QDate(2006, 10, 31);
}
void tst_QItemDelegate::dateTimeEditor()
{
QFETCH(QTime, time);
QFETCH(QDate, date);
QTableWidgetItem *item1 = new QTableWidgetItem;
item1->setData(Qt::DisplayRole, time);
QTableWidgetItem *item2 = new QTableWidgetItem;
item2->setData(Qt::DisplayRole, date);
QTableWidgetItem *item3 = new QTableWidgetItem;
item3->setData(Qt::DisplayRole, QDateTime(date, time));
QTableWidget widget(1, 3);
widget.setItem(0, 0, item1);
widget.setItem(0, 1, item2);
widget.setItem(0, 2, item3);
widget.show();
widget.editItem(item1);
QTestEventLoop::instance().enterLoop(1);
QTimeEdit *timeEditor = qFindChild<QTimeEdit *>(widget.viewport());
QVERIFY(timeEditor);
QCOMPARE(timeEditor->time(), time);
// The data must actually be different in order for the model
// to be updated.
timeEditor->setTime(time.addSecs(60));
widget.clearFocus();
qApp->setActiveWindow(&widget);
widget.setFocus();
widget.editItem(item2);
QTestEventLoop::instance().enterLoop(1);
QDateEdit *dateEditor = qFindChild<QDateEdit *>(widget.viewport());
QVERIFY(dateEditor);
QCOMPARE(dateEditor->date(), date);
dateEditor->setDate(date.addDays(60));
widget.clearFocus();
widget.setFocus();
widget.editItem(item3);
QTestEventLoop::instance().enterLoop(1);
QList<QDateTimeEdit *> dateTimeEditors = widget.findChildren<QDateTimeEdit *>();
QDateTimeEdit *dateTimeEditor = 0;
foreach(dateTimeEditor, dateTimeEditors)
if (dateTimeEditor->metaObject()->className() == QLatin1String("QDateTimeEdit"))
break;
QVERIFY(dateTimeEditor);
QCOMPARE(dateTimeEditor->date(), date);
QCOMPARE(dateTimeEditor->time(), time);
dateTimeEditor->setTime(time.addSecs(600));
widget.clearFocus();
QVERIFY(item1->data(Qt::EditRole).userType() == QMetaType::QTime);
QVERIFY(item2->data(Qt::EditRole).userType() == QMetaType::QDate);
QVERIFY(item3->data(Qt::EditRole).userType() == QMetaType::QDateTime);
}
void tst_QItemDelegate::decoration_data()
{
QTest::addColumn<int>("type");
QTest::addColumn<QSize>("size");
QTest::addColumn<QSize>("expected");
int pm = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
QTest::newRow("pixmap 30x30")
<< (int)QVariant::Pixmap
<< QSize(30, 30)
<< QSize(30, 30);
QTest::newRow("image 30x30")
<< (int)QVariant::Image
<< QSize(30, 30)
<< QSize(30, 30);
//The default engine scales pixmaps down if required, but never up. For WinCE we need bigger IconSize than 30
QTest::newRow("icon 30x30")
<< (int)QVariant::Icon
<< QSize(60, 60)
<< QSize(pm, pm);
QTest::newRow("color 30x30")
<< (int)QVariant::Color
<< QSize(30, 30)
<< QSize(pm, pm);
// This demands too much memory and potentially hangs. Feel free to uncomment
// for your own testing.
// QTest::newRow("pixmap 30x30 big")
// << (int)QVariant::Pixmap
// << QSize(1024, 1024) // Over 1M
// << QSize(1024, 1024);
}
void tst_QItemDelegate::decoration()
{
Q_CHECK_PAINTEVENTS
QFETCH(int, type);
QFETCH(QSize, size);
QFETCH(QSize, expected);
QTableWidget table(1, 1);
TestItemDelegate delegate;
table.setItemDelegate(&delegate);
table.show();
#ifdef Q_WS_X11
qt_x11_wait_for_window_manager(&table);
#endif
QApplication::setActiveWindow(&table);
QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&table));
QVariant value;
switch ((QVariant::Type)type) {
case QVariant::Pixmap: {
QPixmap pm(size);
pm.fill(Qt::black);
value = pm;
break;
}
case QVariant::Image: {
QImage img(size, QImage::Format_Mono);
qMemSet(img.bits(), 0, img.byteCount());
value = img;
break;
}
case QVariant::Icon: {
QPixmap pm(size);
pm.fill(Qt::black);
value = QIcon(pm);
break;
}
case QVariant::Color:
value = QColor(Qt::green);
break;
default:
break;
}
QTableWidgetItem *item = new QTableWidgetItem;
item->setData(Qt::DecorationRole, value);
table.setItem(0, 0, item);
item->setSelected(true);
QApplication::processEvents();
QTRY_COMPARE(delegate.decorationRect.size(), expected);
}
void tst_QItemDelegate::editorEvent_data()
{
QTest::addColumn<QRect>("rect");
QTest::addColumn<QString>("text");
QTest::addColumn<int>("checkState");
QTest::addColumn<int>("flags");
QTest::addColumn<bool>("inCheck");
QTest::addColumn<int>("type");
QTest::addColumn<int>("button");
QTest::addColumn<bool>("edited");
QTest::addColumn<int>("expectedCheckState");
QTest::newRow("unchecked, checkable, release")
<< QRect(0, 0, 20, 20)
<< QString("foo")
<< (int)(Qt::Unchecked)
<< (int)(Qt::ItemIsEditable
|Qt::ItemIsSelectable
|Qt::ItemIsUserCheckable
|Qt::ItemIsEnabled
|Qt::ItemIsDragEnabled
|Qt::ItemIsDropEnabled)
<< true
<< (int)(QEvent::MouseButtonRelease)
<< (int)(Qt::LeftButton)
<< true
<< (int)(Qt::Checked);
QTest::newRow("checked, checkable, release")
<< QRect(0, 0, 20, 20)
<< QString("foo")
<< (int)(Qt::Checked)
<< (int)(Qt::ItemIsEditable
|Qt::ItemIsSelectable
|Qt::ItemIsUserCheckable
|Qt::ItemIsEnabled
|Qt::ItemIsDragEnabled
|Qt::ItemIsDropEnabled)
<< true
<< (int)(QEvent::MouseButtonRelease)
<< (int)(Qt::LeftButton)
<< true
<< (int)(Qt::Unchecked);
QTest::newRow("unchecked, checkable, release")
<< QRect(0, 0, 20, 20)
<< QString("foo")
<< (int)(Qt::Unchecked)
<< (int)(Qt::ItemIsEditable
|Qt::ItemIsSelectable
|Qt::ItemIsUserCheckable
|Qt::ItemIsEnabled
|Qt::ItemIsDragEnabled
|Qt::ItemIsDropEnabled)
<< true
<< (int)(QEvent::MouseButtonRelease)
<< (int)(Qt::LeftButton)
<< true
<< (int)(Qt::Checked);
QTest::newRow("unchecked, checkable, release, right button")
<< QRect(0, 0, 20, 20)
<< QString("foo")
<< (int)(Qt::Unchecked)
<< (int)(Qt::ItemIsEditable
|Qt::ItemIsSelectable
|Qt::ItemIsUserCheckable
|Qt::ItemIsEnabled
|Qt::ItemIsDragEnabled
|Qt::ItemIsDropEnabled)
<< true
<< (int)(QEvent::MouseButtonRelease)
<< (int)(Qt::RightButton)
<< false
<< (int)(Qt::Unchecked);
QTest::newRow("unchecked, checkable, release outside")
<< QRect(0, 0, 20, 20)
<< QString("foo")
<< (int)(Qt::Unchecked)
<< (int)(Qt::ItemIsEditable
|Qt::ItemIsSelectable
|Qt::ItemIsUserCheckable
|Qt::ItemIsEnabled
|Qt::ItemIsDragEnabled
|Qt::ItemIsDropEnabled)
<< false
<< (int)(QEvent::MouseButtonRelease)
<< (int)(Qt::LeftButton)
<< false
<< (int)(Qt::Unchecked);
QTest::newRow("unchecked, checkable, dblclick")
<< QRect(0, 0, 20, 20)
<< QString("foo")
<< (int)(Qt::Unchecked)
<< (int)(Qt::ItemIsEditable
|Qt::ItemIsSelectable
|Qt::ItemIsUserCheckable
|Qt::ItemIsEnabled
|Qt::ItemIsDragEnabled
|Qt::ItemIsDropEnabled)
<< true
<< (int)(QEvent::MouseButtonDblClick)
<< (int)(Qt::LeftButton)
<< true
<< (int)(Qt::Unchecked);
}
void tst_QItemDelegate::editorEvent()
{
QFETCH(QRect, rect);
QFETCH(QString, text);
QFETCH(int, checkState);
QFETCH(int, flags);
QFETCH(bool, inCheck);
QFETCH(int, type);
QFETCH(int, button);
QFETCH(bool, edited);
QFETCH(int, expectedCheckState);
QStandardItemModel model(1, 1);
QModelIndex index = model.index(0, 0);
QVERIFY(index.isValid());
QStandardItem *item = model.itemFromIndex(index);
item->setText(text);
item->setCheckState((Qt::CheckState)checkState);
item->setFlags((Qt::ItemFlags)flags);
QStyleOptionViewItem option;
option.rect = rect;
option.state |= QStyle::State_Enabled;
// mimic QStyledItemDelegate::initStyleOption logic
option.features |= QStyleOptionViewItem::HasCheckIndicator | QStyleOptionViewItem::HasDisplay;
option.checkState = Qt::CheckState(checkState);
const int checkMargin = qApp->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, 0) + 1;
QPoint pos = inCheck ? qApp->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &option, 0).center() + QPoint(checkMargin, 0) : QPoint(200,200);
QEvent *event = new QMouseEvent((QEvent::Type)type,
pos,
(Qt::MouseButton)button,
(Qt::MouseButton)button,
Qt::NoModifier);
TestItemDelegate delegate;
bool wasEdited = delegate.editorEvent(event, &model, option, index);
delete event;
QApplication::processEvents();
QCOMPARE(wasEdited, edited);
QCOMPARE(index.data(Qt::CheckStateRole).toInt(), expectedCheckState);
}
enum WidgetType
{
LineEdit,
TextEdit,
PlainTextEdit
};
Q_DECLARE_METATYPE(WidgetType);
void tst_QItemDelegate::enterKey_data()
{
QTest::addColumn<WidgetType>("widget");
QTest::addColumn<int>("key");
QTest::addColumn<bool>("expectedFocus");
QTest::newRow("lineedit enter") << LineEdit << int(Qt::Key_Enter) << false;
QTest::newRow("textedit enter") << TextEdit << int(Qt::Key_Enter) << true;
QTest::newRow("plaintextedit enter") << PlainTextEdit << int(Qt::Key_Enter) << true;
QTest::newRow("plaintextedit return") << PlainTextEdit << int(Qt::Key_Return) << true;
QTest::newRow("plaintextedit tab") << PlainTextEdit << int(Qt::Key_Tab) << false;
QTest::newRow("lineedit tab") << LineEdit << int(Qt::Key_Tab) << false;
}
void tst_QItemDelegate::enterKey()
{
QFETCH(WidgetType, widget);
QFETCH(int, key);
QFETCH(bool, expectedFocus);
QStandardItemModel model;
model.appendRow(new QStandardItem());
QListView view;
view.setModel(&model);
view.show();
QApplication::setActiveWindow(&view);
view.setFocus();
QTest::qWait(30);
struct TestDelegate : public QItemDelegate
{
WidgetType widgetType;
virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const
{
QWidget *editor = 0;
switch(widgetType) {
case LineEdit:
editor = new QLineEdit(parent);
break;
case TextEdit:
editor = new QTextEdit(parent);
break;
case PlainTextEdit:
editor = new QPlainTextEdit(parent);
break;
}
editor->setObjectName(QString::fromLatin1("TheEditor"));
return editor;
}
} delegate;
delegate.widgetType = widget;
view.setItemDelegate(&delegate);
QModelIndex index = model.index(0, 0);
view.setCurrentIndex(index); // the editor will only selectAll on the current index
view.edit(index);
QTest::qWait(30);
QList<QWidget*> lineEditors = qFindChildren<QWidget *>(view.viewport(), QString::fromLatin1("TheEditor"));
QCOMPARE(lineEditors.count(), 1);
QPointer<QWidget> editor = lineEditors.at(0);
QCOMPARE(editor->hasFocus(), true);
QTest::keyClick(editor, Qt::Key(key));
QApplication::processEvents();
// The line edit has already been destroyed, so avoid that case.
if (widget == TextEdit || widget == PlainTextEdit) {
QVERIFY(!editor.isNull());
QCOMPARE(editor && editor->hasFocus(), expectedFocus);
}
}
void tst_QItemDelegate::task257859_finalizeEdit()
{
QStandardItemModel model;
model.appendRow(new QStandardItem());
QListView view;
view.setModel(&model);
view.show();
QApplication::setActiveWindow(&view);
view.setFocus();
QTest::qWait(30);
QModelIndex index = model.index(0, 0);
view.edit(index);
QTest::qWait(30);
QList<QLineEdit *> lineEditors = qFindChildren<QLineEdit *>(view.viewport());
QCOMPARE(lineEditors.count(), 1);
QPointer<QWidget> editor = lineEditors.at(0);
QCOMPARE(editor->hasFocus(), true);
QDialog dialog;
QTimer::singleShot(500, &dialog, SLOT(close()));
dialog.exec();
QTRY_VERIFY(!editor);
}
void tst_QItemDelegate::QTBUG4435_keepSelectionOnCheck()
{
QStandardItemModel model(3, 1);
for (int i = 0; i < 3; ++i) {
QStandardItem *item = new QStandardItem(QLatin1String("Item ") + QString::number(i));
item->setCheckable(true);
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
model.setItem(i, item);
}
QTableView view;
view.setModel(&model);
view.setItemDelegate(new TestItemDelegate);
view.show();
view.selectAll();
QTest::qWaitForWindowShown(&view);
QStyleOptionViewItem option;
option.rect = view.visualRect(model.index(0, 0));
// mimic QStyledItemDelegate::initStyleOption logic
option.features = QStyleOptionViewItem::HasDisplay | QStyleOptionViewItem::HasCheckIndicator;
option.checkState = Qt::CheckState(model.index(0, 0).data(Qt::CheckStateRole).toInt());
const int checkMargin = qApp->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, 0) + 1;
QPoint pos = qApp->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &option, 0).center()
+ QPoint(checkMargin, 0);
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, pos);
QTRY_VERIFY(view.selectionModel()->isColumnSelected(0, QModelIndex()));
QCOMPARE(model.item(0)->checkState(), Qt::Checked);
}
// ### _not_ covered:
// editing with a custom editor factory
// painting when editing
// painting elided text
// painting wrapped text
// painting focus
// painting icon
// painting color
// painting check
// painting selected
// rect for invalid
// rect for pixmap
// rect for image
// rect for icon
// rect for check
QTEST_MAIN(tst_QItemDelegate)
#include "tst_qitemdelegate.moc"