mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2024-11-23 20:10:48 +08:00
1906 lines
67 KiB
C++
1906 lines
67 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include <QTest>
|
|
#include <QtGui>
|
|
#include <QtWidgets>
|
|
#include <QtDebug>
|
|
#include <QPair>
|
|
#include <QList>
|
|
#include <QPointer>
|
|
#include <QSignalSpy>
|
|
|
|
#include <QtTest/private/qtesthelpers_p.h>
|
|
|
|
#include "../../../../shared/filesystem.h"
|
|
|
|
#include <memory>
|
|
|
|
#include <QtWidgets/private/qapplication_p.h>
|
|
|
|
Q_DECLARE_METATYPE(QCompleter::CompletionMode)
|
|
|
|
using namespace QTestPrivate;
|
|
|
|
class CsvCompleter : public QCompleter
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
using QCompleter::QCompleter;
|
|
|
|
QString pathFromIndex(const QModelIndex& sourceIndex) const override;
|
|
|
|
void setCsvCompletion(bool set) { csv = set; }
|
|
|
|
protected:
|
|
QStringList splitPath(const QString &path) const override
|
|
{
|
|
return csv ? path.split(QLatin1Char(',')) : QCompleter::splitPath(path);
|
|
}
|
|
|
|
private:
|
|
bool csv = true;
|
|
};
|
|
|
|
QString CsvCompleter::pathFromIndex(const QModelIndex &sourceIndex) const
|
|
{
|
|
if (!csv)
|
|
return QCompleter::pathFromIndex(sourceIndex);
|
|
|
|
if (!sourceIndex.isValid())
|
|
return QString();
|
|
|
|
QModelIndex idx = sourceIndex;
|
|
QStringList list;
|
|
do {
|
|
QString t = model()->data(idx, completionRole()).toString();
|
|
list.prepend(t);
|
|
QModelIndex parent = idx.parent();
|
|
idx = parent.sibling(parent.row(), sourceIndex.column());
|
|
} while (idx.isValid());
|
|
|
|
return list.size() == 1 ? list.constFirst() : list.join(QLatin1Char(','));
|
|
}
|
|
|
|
class tst_QCompleter : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
tst_QCompleter();
|
|
~tst_QCompleter();
|
|
|
|
private slots:
|
|
void getSetCheck();
|
|
|
|
void multipleWidgets();
|
|
void focusIn();
|
|
|
|
void csMatchingOnCsSortedModel_data();
|
|
void csMatchingOnCsSortedModel();
|
|
void ciMatchingOnCiSortedModel_data();
|
|
void ciMatchingOnCiSortedModel();
|
|
|
|
void ciMatchingOnCsSortedModel_data();
|
|
void ciMatchingOnCsSortedModel();
|
|
void csMatchingOnCiSortedModel_data();
|
|
void csMatchingOnCiSortedModel();
|
|
|
|
void fileSystemModel_data();
|
|
void fileSystemModel();
|
|
void fileDialog_data();
|
|
void fileDialog();
|
|
|
|
void changingModel_data();
|
|
void changingModel();
|
|
|
|
void sortedEngineRowCount_data();
|
|
void sortedEngineRowCount();
|
|
void unsortedEngineRowCount_data();
|
|
void unsortedEngineRowCount();
|
|
|
|
void currentRow();
|
|
void sortedEngineMapFromSource();
|
|
void unsortedEngineMapFromSource();
|
|
|
|
void historySearch();
|
|
|
|
void modelDeletion();
|
|
void setters();
|
|
|
|
void dynamicSortOrder();
|
|
void disabledItems();
|
|
|
|
// task-specific tests below me
|
|
void task178797_activatedOnReturn();
|
|
void task189564_omitNonSelectableItems();
|
|
void task246056_setCompletionPrefix();
|
|
void task250064_lostFocus();
|
|
|
|
void task253125_lineEditCompletion_data();
|
|
void task253125_lineEditCompletion();
|
|
void task247560_keyboardNavigation();
|
|
void QTBUG_14292_filesystem();
|
|
void QTBUG_52028_tabAutoCompletes();
|
|
void QTBUG_51889_activatedSentTwice();
|
|
void showPopupInGraphicsView();
|
|
void inheritedEventFilter();
|
|
|
|
private:
|
|
void filter(bool assync = false);
|
|
void testRowCount();
|
|
enum ModelType {
|
|
CASE_SENSITIVELY_SORTED_MODEL,
|
|
CASE_INSENSITIVELY_SORTED_MODEL,
|
|
HISTORY_MODEL,
|
|
FILESYSTEM_MODEL
|
|
};
|
|
void setSourceModel(ModelType);
|
|
|
|
CsvCompleter *completer = nullptr;
|
|
QTreeWidget *treeWidget;
|
|
const int completionColumn = 0;
|
|
const int columnCount = 3;
|
|
};
|
|
|
|
tst_QCompleter::tst_QCompleter() : treeWidget(new QTreeWidget)
|
|
{
|
|
treeWidget->move(100, 100);
|
|
treeWidget->setColumnCount(columnCount);
|
|
}
|
|
|
|
tst_QCompleter::~tst_QCompleter()
|
|
{
|
|
delete treeWidget;
|
|
delete completer;
|
|
}
|
|
|
|
#ifdef Q_OS_ANDROID
|
|
static QString androidHomePath()
|
|
{
|
|
const auto homePaths = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
|
|
QDir dir = homePaths.isEmpty() ? QDir() : homePaths.first();
|
|
dir.cdUp();
|
|
return dir.path();
|
|
}
|
|
#endif
|
|
|
|
void tst_QCompleter::setSourceModel(ModelType type)
|
|
{
|
|
QTreeWidgetItem *parent, *child;
|
|
treeWidget->clear();
|
|
switch(type) {
|
|
case CASE_SENSITIVELY_SORTED_MODEL:
|
|
// Creates a tree model with top level items P0, P1, .., p0, p1,..
|
|
// Each of these items parents have children (for P0 - c0P0, c1P0,...)
|
|
for (int i = 0; i < 2; i++) {
|
|
for (int j = 0; j < 5; j++) {
|
|
parent = new QTreeWidgetItem(treeWidget);
|
|
const QString text = QLatin1Char(i == 0 ? 'P' : 'p') + QString::number(j);
|
|
parent->setText(completionColumn, text);
|
|
for (int k = 0; k < 5; k++) {
|
|
child = new QTreeWidgetItem(parent);
|
|
QString t = QLatin1Char('c') + QString::number(k) + text;
|
|
child->setText(completionColumn, t);
|
|
}
|
|
}
|
|
}
|
|
completer->setModel(treeWidget->model());
|
|
completer->setCompletionColumn(completionColumn);
|
|
break;
|
|
case CASE_INSENSITIVELY_SORTED_MODEL:
|
|
case HISTORY_MODEL:
|
|
// Creates a tree model with top level items P0, p0, P1, p1,...
|
|
// Each of these items have children c0p0, c1p0,..
|
|
for (int i = 0; i < 5; i++) {
|
|
for (int j = 0; j < 2; j++) {
|
|
parent = new QTreeWidgetItem(treeWidget);
|
|
const QString text = QLatin1Char(j == 0 ? 'P' : 'p') + QString::number(i);
|
|
parent->setText(completionColumn, text);
|
|
for (int k = 0; k < 5; k++) {
|
|
child = new QTreeWidgetItem(parent);
|
|
QString t = QLatin1Char('c') + QString::number(k) + text;
|
|
child->setText(completionColumn, t);
|
|
}
|
|
}
|
|
}
|
|
completer->setModel(treeWidget->model());
|
|
completer->setCompletionColumn(completionColumn);
|
|
if (type == CASE_INSENSITIVELY_SORTED_MODEL)
|
|
break;
|
|
parent = new QTreeWidgetItem(treeWidget);
|
|
parent->setText(completionColumn, QLatin1String("p3,c3p3"));
|
|
parent = new QTreeWidgetItem(treeWidget);
|
|
parent->setText(completionColumn, QLatin1String("p2,c4p2"));
|
|
break;
|
|
case FILESYSTEM_MODEL:
|
|
completer->setCsvCompletion(false);
|
|
{
|
|
auto m = new QFileSystemModel(completer);
|
|
#ifdef Q_OS_ANDROID
|
|
// Android 11 and above doesn't allow accessing root filesystem as before,
|
|
// so let's opt int for the app's home.
|
|
m->setRootPath(androidHomePath());
|
|
#else
|
|
m->setRootPath("/");
|
|
#endif
|
|
completer->setModel(m);
|
|
}
|
|
completer->setCompletionColumn(0);
|
|
break;
|
|
default:
|
|
qDebug() << "Invalid type";
|
|
break;
|
|
}
|
|
}
|
|
|
|
void tst_QCompleter::filter(bool assync)
|
|
{
|
|
QFETCH(QString, filterText);
|
|
QFETCH(const QString, step);
|
|
QFETCH(QString, completion);
|
|
QFETCH(QString, completionText);
|
|
|
|
if (filterText.compare("FILTERING_OFF", Qt::CaseInsensitive) == 0) {
|
|
completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
|
|
return;
|
|
}
|
|
|
|
int result = -1;
|
|
const int attempts = assync ? 10 : 1;
|
|
for (int times = 0; times < attempts; ++times) {
|
|
completer->setCompletionPrefix(filterText);
|
|
|
|
for (QChar s : step) {
|
|
int row = completer->currentRow();
|
|
switch (s.toUpper().toLatin1()) {
|
|
case 'P':
|
|
--row;
|
|
break;
|
|
case 'N':
|
|
++row;
|
|
break;
|
|
case 'L':
|
|
row = completer->completionCount() - 1;
|
|
break;
|
|
case 'F':
|
|
row = 0;
|
|
break;
|
|
default:
|
|
QFAIL(qPrintable(QString(
|
|
"Problem with 'step' value in test data: %1 (only P, N, L and F are allowed)."
|
|
).arg(s)));
|
|
}
|
|
completer->setCurrentRow(row);
|
|
}
|
|
|
|
result = QString::compare(completer->currentCompletion(), completionText,
|
|
completer->caseSensitivity());
|
|
if (result == 0)
|
|
break;
|
|
if (assync)
|
|
QTest::qWait(50 * times);
|
|
}
|
|
|
|
QCOMPARE(result, 0);
|
|
}
|
|
|
|
// Testing get/set functions
|
|
void tst_QCompleter::getSetCheck()
|
|
{
|
|
QStandardItemModel standardItemModel(3,3);
|
|
QCompleter completer(&standardItemModel);
|
|
|
|
// QString QCompleter::completionPrefix()
|
|
// void QCompleter::setCompletionPrefix(QString)
|
|
completer.setCompletionPrefix(QString("te"));
|
|
QCOMPARE(completer.completionPrefix(), QString("te"));
|
|
completer.setCompletionPrefix(QString());
|
|
QCOMPARE(completer.completionPrefix(), QString());
|
|
|
|
// ModelSorting QCompleter::modelSorting()
|
|
// void QCompleter::setModelSorting(ModelSorting)
|
|
completer.setModelSorting(QCompleter::CaseSensitivelySortedModel);
|
|
QCOMPARE(completer.modelSorting(), QCompleter::CaseSensitivelySortedModel);
|
|
completer.setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
|
QCOMPARE(completer.modelSorting(), QCompleter::CaseInsensitivelySortedModel);
|
|
completer.setModelSorting(QCompleter::UnsortedModel);
|
|
QCOMPARE(completer.modelSorting(), QCompleter::UnsortedModel);
|
|
|
|
// CompletionMode QCompleter::completionMode()
|
|
// void QCompleter::setCompletionMode(CompletionMode)
|
|
QCOMPARE(completer.completionMode(), QCompleter::PopupCompletion); // default value
|
|
completer.setCompletionMode(QCompleter::UnfilteredPopupCompletion);
|
|
QCOMPARE(completer.completionMode(), QCompleter::UnfilteredPopupCompletion);
|
|
completer.setCompletionMode(QCompleter::InlineCompletion);
|
|
QCOMPARE(completer.completionMode(), QCompleter::InlineCompletion);
|
|
|
|
// int QCompleter::completionColumn()
|
|
// void QCompleter::setCompletionColumn(int)
|
|
completer.setCompletionColumn(2);
|
|
QCOMPARE(completer.completionColumn(), 2);
|
|
completer.setCompletionColumn(1);
|
|
QCOMPARE(completer.completionColumn(), 1);
|
|
|
|
// int QCompleter::completionRole()
|
|
// void QCompleter::setCompletionRole(int)
|
|
QCOMPARE(completer.completionRole(), static_cast<int>(Qt::EditRole)); // default value
|
|
completer.setCompletionRole(Qt::DisplayRole);
|
|
QCOMPARE(completer.completionRole(), static_cast<int>(Qt::DisplayRole));
|
|
|
|
// int QCompleter::maxVisibleItems()
|
|
// void QCompleter::setMaxVisibleItems(int)
|
|
QCOMPARE(completer.maxVisibleItems(), 7); // default value
|
|
completer.setMaxVisibleItems(10);
|
|
QCOMPARE(completer.maxVisibleItems(), 10);
|
|
QTest::ignoreMessage(QtWarningMsg, "QCompleter::setMaxVisibleItems: "
|
|
"Invalid max visible items (-2147483648) must be >= 0");
|
|
completer.setMaxVisibleItems(INT_MIN);
|
|
QCOMPARE(completer.maxVisibleItems(), 10); // Cannot be set to something negative => old value
|
|
|
|
// Qt::CaseSensitivity QCompleter::caseSensitivity()
|
|
// void QCompleter::setCaseSensitivity(Qt::CaseSensitivity)
|
|
QCOMPARE(completer.caseSensitivity(), Qt::CaseSensitive); // default value
|
|
completer.setCaseSensitivity(Qt::CaseInsensitive);
|
|
QCOMPARE(completer.caseSensitivity(), Qt::CaseInsensitive);
|
|
|
|
// bool QCompleter::wrapAround()
|
|
// void QCompleter::setWrapAround(bool)
|
|
QCOMPARE(completer.wrapAround(), true); // default value
|
|
completer.setWrapAround(false);
|
|
QCOMPARE(completer.wrapAround(), false);
|
|
|
|
#if QT_CONFIG(filesystemmodel)
|
|
// QTBUG-54642, changing from QFileSystemModel to another model should restore role.
|
|
completer.setCompletionRole(Qt::EditRole);
|
|
QCOMPARE(completer.completionRole(), static_cast<int>(Qt::EditRole)); // default value
|
|
QFileSystemModel fileSystemModel;
|
|
completer.setModel(&fileSystemModel);
|
|
QCOMPARE(completer.completionRole(), static_cast<int>(QFileSystemModel::FileNameRole));
|
|
completer.setModel(&standardItemModel);
|
|
QCOMPARE(completer.completionRole(), static_cast<int>(Qt::EditRole));
|
|
completer.setCompletionRole(Qt::ToolTipRole);
|
|
QStandardItemModel standardItemModel2(2, 2); // Do not clobber a custom role when changing models
|
|
completer.setModel(&standardItemModel2);
|
|
QCOMPARE(completer.completionRole(), static_cast<int>(Qt::ToolTipRole));
|
|
#endif // QT_CONFIG(filesystemmodel)
|
|
}
|
|
|
|
void tst_QCompleter::csMatchingOnCsSortedModel_data()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setModelSorting(QCompleter::CaseSensitivelySortedModel);
|
|
completer->setCaseSensitivity(Qt::CaseSensitive);
|
|
setSourceModel(CASE_SENSITIVELY_SORTED_MODEL);
|
|
|
|
QTest::addColumn<QString>("filterText");
|
|
QTest::addColumn<QString>("step");
|
|
QTest::addColumn<QString>("completion");
|
|
QTest::addColumn<QString>("completionText");
|
|
|
|
#define ROWNAME(name) ((QByteArray(name) + ' ' + QByteArray::number(i)).constData())
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
if (i == 1)
|
|
QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << "";
|
|
|
|
// Plain text filter
|
|
QTest::newRow(ROWNAME("()")) << "" << "" << "P0" << "P0";
|
|
QTest::newRow(ROWNAME("()F")) << "" << "F" << "P0" << "P0";
|
|
QTest::newRow(ROWNAME("()L")) << "" << "L" << "p4" << "p4";
|
|
QTest::newRow(ROWNAME("()N")) << "" << "N" << "P1" << "P1";
|
|
QTest::newRow(ROWNAME("(P)")) << "P" << "" << "P0" << "P0";
|
|
QTest::newRow(ROWNAME("(P)F")) << "P" << "" << "P0" << "P0";
|
|
QTest::newRow(ROWNAME("(P)L")) << "P" << "L" << "P4" << "P4";
|
|
QTest::newRow(ROWNAME("(p)")) << "p" << "" << "p0" << "p0";
|
|
QTest::newRow(ROWNAME("(p)N")) << "p" << "N" << "p1" << "p1";
|
|
QTest::newRow(ROWNAME("(p)NN")) << "p" << "NN" << "p2" << "p2";
|
|
QTest::newRow(ROWNAME("(p)NNN")) << "p" << "NNN" << "p3" << "p3";
|
|
QTest::newRow(ROWNAME("(p)NNNN")) << "p" << "NNNN" << "p4" << "p4";
|
|
QTest::newRow(ROWNAME("(p1)")) << "p1" << "" << "p1" << "p1";
|
|
QTest::newRow(ROWNAME("(p11)")) << "p11" << "" << "" << "";
|
|
|
|
// Tree filter
|
|
QTest::newRow(ROWNAME("(P0,)")) << "P0," << "" << "c0P0" << "P0,c0P0";
|
|
QTest::newRow(ROWNAME("(P0,c)")) << "P0,c" << "" << "c0P0" << "P0,c0P0";
|
|
QTest::newRow(ROWNAME("(P0,c1)")) << "P0,c1" << "" << "c1P0" << "P0,c1P0";
|
|
QTest::newRow(ROWNAME("(P0,c3P0)")) << "P0,c3P0" << "" << "c3P0" << "P0,c3P0";
|
|
QTest::newRow(ROWNAME("(P3,c)F")) << "P3,c" << "F" << "c0P3" << "P3,c0P3";
|
|
QTest::newRow(ROWNAME("(P3,c)L")) << "P3,c" << "L" << "c4P3" << "P3,c4P3";
|
|
QTest::newRow(ROWNAME("(P3,c)N")) << "P3,c" << "N" << "c1P3" << "P3,c1P3";
|
|
QTest::newRow(ROWNAME("(P3,c)NN")) << "P3,c" << "NN" << "c2P3" << "P3,c2P3";
|
|
QTest::newRow(ROWNAME("(P3,,c)")) << "P3,,c" << "" << "" << "";
|
|
QTest::newRow(ROWNAME("(P3,c0P3,)")) << "P3,c0P3," << "" << "" << "";
|
|
QTest::newRow(ROWNAME("(P,)")) << "P," << "" << "" << "";
|
|
}
|
|
#undef ROWNAME
|
|
}
|
|
|
|
void tst_QCompleter::csMatchingOnCsSortedModel()
|
|
{
|
|
filter();
|
|
}
|
|
|
|
void tst_QCompleter::ciMatchingOnCiSortedModel_data()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
|
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
|
setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL);
|
|
|
|
QTest::addColumn<QString>("filterText");
|
|
QTest::addColumn<QString>("step");
|
|
QTest::addColumn<QString>("completion");
|
|
QTest::addColumn<QString>("completionText");
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
if (i == 1)
|
|
QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << "";
|
|
|
|
// Plain text filter
|
|
QTest::newRow("()") << "" << "" << "P0" << "P0";
|
|
QTest::newRow("()F") << "" << "F" << "P0" << "P0";
|
|
QTest::newRow("()L") << "" << "L" << "p4" << "p4";
|
|
QTest::newRow("()N") << "" << "N" << "p0" << "p0";
|
|
QTest::newRow("(P)") << "P" << "" << "P0" << "P0";
|
|
QTest::newRow("(P)F") << "P" << "" << "P0" << "P0";
|
|
QTest::newRow("(P)L") << "P" << "L" << "p4" << "p4";
|
|
QTest::newRow("(p)") << "p" << "" << "P0" << "P0";
|
|
QTest::newRow("(p)N") << "p" << "N" << "p0" << "p0";
|
|
QTest::newRow("(p)NN") << "p" << "NN" << "P1" << "P1";
|
|
QTest::newRow("(p)NNN") << "p" << "NNN" << "p1" << "p1";
|
|
QTest::newRow("(p1)") << "p1" << "" << "P1" << "P1";
|
|
QTest::newRow("(p1)N") << "p1" << "N" << "p1" << "p1";
|
|
QTest::newRow("(p11)") << "p11" << "" << "" << "";
|
|
|
|
//// Tree filter
|
|
QTest::newRow("(p0,)") << "p0," << "" << "c0P0" << "P0,c0P0";
|
|
QTest::newRow("(p0,c)") << "p0,c" << "" << "c0P0" << "P0,c0P0";
|
|
QTest::newRow("(p0,c1)") << "p0,c1" << "" << "c1P0" << "P0,c1P0";
|
|
QTest::newRow("(p0,c3P0)") << "p0,c3P0" << "" << "c3P0" << "P0,c3P0";
|
|
QTest::newRow("(p3,c)F") << "p3,c" << "F" << "c0P3" << "P3,c0P3";
|
|
QTest::newRow("(p3,c)L") << "p3,c" << "L" << "c4P3" << "P3,c4P3";
|
|
QTest::newRow("(p3,c)N") << "p3,c" << "N" << "c1P3" << "P3,c1P3";
|
|
QTest::newRow("(p3,c)NN") << "p3,c" << "NN" << "c2P3" << "P3,c2P3";
|
|
QTest::newRow("(p3,,c)") << "p3,,c" << "" << "" << "";
|
|
QTest::newRow("(p3,c0P3,)") << "p3,c0P3," << "" << "" << "";
|
|
QTest::newRow("(p,)") << "p," << "" << "" << "";
|
|
}
|
|
}
|
|
|
|
void tst_QCompleter::ciMatchingOnCiSortedModel()
|
|
{
|
|
filter();
|
|
}
|
|
|
|
void tst_QCompleter::ciMatchingOnCsSortedModel_data()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setModelSorting(QCompleter::CaseSensitivelySortedModel);
|
|
setSourceModel(CASE_SENSITIVELY_SORTED_MODEL);
|
|
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
|
|
|
QTest::addColumn<QString>("filterText");
|
|
QTest::addColumn<QString>("step");
|
|
QTest::addColumn<QString>("completion");
|
|
QTest::addColumn<QString>("completionText");
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
if (i == 1)
|
|
QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << "";
|
|
|
|
// Plain text filter
|
|
QTest::newRow("()") << "" << "" << "P0" << "P0";
|
|
QTest::newRow("()F") << "" << "F" << "P0" << "P0";
|
|
QTest::newRow("()L") << "" << "L" << "p4" << "p4";
|
|
QTest::newRow("(P)") << "P" << "" << "P0" << "P0";
|
|
QTest::newRow("(P)F") << "P" << "" << "P0" << "P0";
|
|
QTest::newRow("(P)L") << "P" << "L" << "p4" << "p4";
|
|
QTest::newRow("(p)") << "p" << "" << "P0" << "P0";
|
|
QTest::newRow("(p)N") << "p" << "N" << "P1" << "P1";
|
|
QTest::newRow("(p)NN") << "p" << "NN" << "P2" << "P2";
|
|
QTest::newRow("(p)NNN") << "p" << "NNN" << "P3" << "P3";
|
|
QTest::newRow("(p1)") << "p1" << "" << "P1" << "P1";
|
|
QTest::newRow("(p1)N") << "p1" << "N" << "p1" << "p1";
|
|
QTest::newRow("(p11)") << "p11" << "" << "" << "";
|
|
|
|
// Tree filter
|
|
QTest::newRow("(p0,)") << "p0," << "" << "c0P0" << "P0,c0P0";
|
|
QTest::newRow("(p0,c)") << "p0,c" << "" << "c0P0" << "P0,c0P0";
|
|
QTest::newRow("(p0,c1)") << "p0,c1" << "" << "c1P0" << "P0,c1P0";
|
|
QTest::newRow("(p0,c3P0)") << "p0,c3P0" << "" << "c3P0" << "P0,c3P0";
|
|
QTest::newRow("(p3,c)F") << "p3,c" << "F" << "c0P3" << "P3,c0P3";
|
|
QTest::newRow("(p3,c)L") << "p3,c" << "L" << "c4P3" << "P3,c4P3";
|
|
QTest::newRow("(p3,c)N") << "p3,c" << "N" << "c1P3" << "P3,c1P3";
|
|
QTest::newRow("(p3,c)NN") << "p3,c" << "NN" << "c2P3" << "P3,c2P3";
|
|
QTest::newRow("(p3,,c)") << "p3,,c" << "" << "" << "";
|
|
QTest::newRow("(p3,c0P3,)") << "p3,c0P3," << "" << "" << "";
|
|
QTest::newRow("(p,)") << "p," << "" << "" << "";
|
|
}
|
|
}
|
|
|
|
void tst_QCompleter::ciMatchingOnCsSortedModel()
|
|
{
|
|
filter();
|
|
}
|
|
|
|
void tst_QCompleter::csMatchingOnCiSortedModel_data()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
|
setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL);
|
|
completer->setCaseSensitivity(Qt::CaseSensitive);
|
|
|
|
QTest::addColumn<QString>("filterText");
|
|
QTest::addColumn<QString>("step");
|
|
QTest::addColumn<QString>("completion");
|
|
QTest::addColumn<QString>("completionText");
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
if (i == 1)
|
|
QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << "";
|
|
|
|
// Plain text filter
|
|
QTest::newRow("()") << "" << "" << "P0" << "P0";
|
|
QTest::newRow("()F") << "" << "F" << "P0" << "P0";
|
|
QTest::newRow("()L") << "" << "L" << "p4" << "p4";
|
|
QTest::newRow("()N") << "" << "N" << "p0" << "p0";
|
|
QTest::newRow("(P)") << "P" << "" << "P0" << "P0";
|
|
QTest::newRow("(P)F") << "P" << "" << "P0" << "P0";
|
|
QTest::newRow("(P)L") << "P" << "L" << "P4" << "P4";
|
|
QTest::newRow("(p)") << "p" << "" << "p0" << "p0";
|
|
QTest::newRow("(p)N") << "p" << "N" << "p1" << "p1";
|
|
QTest::newRow("(p)NN") << "p" << "NN" << "p2" << "p2";
|
|
QTest::newRow("(p)NNN") << "p" << "NNN" << "p3" << "p3";
|
|
QTest::newRow("(p1)") << "p1" << "" << "p1" << "p1";
|
|
QTest::newRow("(p11)") << "p11" << "" << "" << "";
|
|
|
|
//// Tree filter
|
|
QTest::newRow("(p0,)") << "p0," << "" << "c0p0" << "p0,c0p0";
|
|
QTest::newRow("(p0,c)") << "p0,c" << "" << "c0p0" << "p0,c0p0";
|
|
QTest::newRow("(p0,c1)") << "p0,c1" << "" << "c1p0" << "p0,c1p0";
|
|
QTest::newRow("(p0,c3P0)") << "p0,c3p0" << "" << "c3p0" << "p0,c3p0";
|
|
QTest::newRow("(p3,c)F") << "p3,c" << "F" << "c0p3" << "p3,c0p3";
|
|
QTest::newRow("(p3,c)L") << "p3,c" << "L" << "c4p3" << "p3,c4p3";
|
|
QTest::newRow("(p3,c)N") << "p3,c" << "N" << "c1p3" << "p3,c1p3";
|
|
QTest::newRow("(p3,c)NN") << "p3,c" << "NN" << "c2p3" << "p3,c2p3";
|
|
QTest::newRow("(p3,,c)") << "p3,,c" << "" << "" << "";
|
|
QTest::newRow("(p3,c0P3,)") << "p3,c0P3," << "" << "" << "";
|
|
QTest::newRow("(p,)") << "p," << "" << "" << "";
|
|
|
|
QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << "";
|
|
}
|
|
}
|
|
|
|
void tst_QCompleter::csMatchingOnCiSortedModel()
|
|
{
|
|
filter();
|
|
}
|
|
|
|
void tst_QCompleter::fileSystemModel_data()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setModelSorting(QCompleter::CaseSensitivelySortedModel);
|
|
setSourceModel(FILESYSTEM_MODEL);
|
|
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
|
|
|
QTest::addColumn<QString>("filterText");
|
|
QTest::addColumn<QString>("step");
|
|
QTest::addColumn<QString>("completion");
|
|
QTest::addColumn<QString>("completionText");
|
|
|
|
// NOTE: Add tests carefully, ensurely the paths exist on all systems
|
|
// Output is the sourceText; currentCompletionText()
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
if (i == 1)
|
|
QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << "";
|
|
|
|
#if defined(Q_OS_WIN)
|
|
QTest::newRow("()") << "C" << "" << "C:" << "C:";
|
|
QTest::newRow("()") << "C:\\Program" << "" << "Program Files" << "C:\\Program Files";
|
|
#elif defined (Q_OS_MAC)
|
|
QTest::newRow("()") << "" << "" << "/" << "/";
|
|
QTest::newRow("(/a)") << "/a" << "" << "Applications" << "/Applications";
|
|
// QTest::newRow("(/d)") << "/d" << "" << "Developer" << "/Developer";
|
|
#elif defined(Q_OS_ANDROID)
|
|
QTest::newRow("()") << "" << "" << "/" << "/";
|
|
const QString androidDir = androidHomePath();
|
|
const QString tag = QStringLiteral("%1/fil").arg(androidDir);
|
|
QTest::newRow(tag.toUtf8().data()) << tag << "" << "files" << androidDir + "/files";
|
|
#else
|
|
QTest::newRow("()") << "" << "" << "/" << "/";
|
|
#if !defined(Q_OS_AIX) && !defined(Q_OS_HPUX) && !defined(Q_OS_QNX)
|
|
QTest::newRow("(/h)") << "/h" << "" << "home" << "/home";
|
|
#endif
|
|
QTest::newRow("(/et)") << "/et" << "" << "etc" << "/etc";
|
|
QTest::newRow("(/etc/passw)") << "/etc/passw" << "" << "passwd" << "/etc/passwd";
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void tst_QCompleter::fileSystemModel()
|
|
{
|
|
//QFileSystemModel is async.
|
|
filter(true);
|
|
}
|
|
|
|
/*!
|
|
In the file dialog, the completer uses the EditRole.
|
|
See QTBUG-94799
|
|
*/
|
|
void tst_QCompleter::fileDialog_data()
|
|
{
|
|
fileSystemModel_data();
|
|
completer->setCompletionRole(Qt::EditRole);
|
|
}
|
|
|
|
void tst_QCompleter::fileDialog()
|
|
{
|
|
//QFileSystemModel is async.
|
|
filter(true);
|
|
}
|
|
|
|
|
|
void tst_QCompleter::changingModel_data()
|
|
{
|
|
}
|
|
|
|
void tst_QCompleter::changingModel()
|
|
{
|
|
for (int i = 0; i < 2; i++) {
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setModelSorting(QCompleter::CaseSensitivelySortedModel);
|
|
completer->setCaseSensitivity(Qt::CaseSensitive);
|
|
setSourceModel(CASE_SENSITIVELY_SORTED_MODEL);
|
|
|
|
if (i == 1) {
|
|
completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
|
|
}
|
|
|
|
completer->setCompletionPrefix("p");
|
|
completer->setCurrentRow(completer->completionCount() - 1);
|
|
QCOMPARE(completer->currentCompletion(), QString("p4"));
|
|
|
|
// Test addition of data
|
|
QTreeWidgetItem p5item;
|
|
p5item.setText(completionColumn, "p5");
|
|
treeWidget->addTopLevelItem(&p5item);
|
|
completer->setCompletionPrefix("p5");
|
|
QCOMPARE(completer->currentCompletion(), QString("p5"));
|
|
|
|
// Test removal of data
|
|
int p5index = treeWidget->indexOfTopLevelItem(&p5item);
|
|
treeWidget->takeTopLevelItem(p5index);
|
|
QCOMPARE(completer->currentCompletion(), QString(""));
|
|
|
|
// Test clear
|
|
treeWidget->clear();
|
|
QCOMPARE(completer->currentIndex(), QModelIndex());
|
|
}
|
|
}
|
|
|
|
void tst_QCompleter::testRowCount()
|
|
{
|
|
QFETCH(QString, filterText);
|
|
QFETCH(bool, hasChildren);
|
|
QFETCH(int, rowCount);
|
|
QFETCH(int, completionCount);
|
|
|
|
if (filterText.compare("FILTERING_OFF", Qt::CaseInsensitive) == 0) {
|
|
completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
|
|
return;
|
|
}
|
|
|
|
completer->setCompletionPrefix(filterText);
|
|
const QAbstractItemModel *completionModel = completer->completionModel();
|
|
QCOMPARE(completionModel->rowCount(), rowCount);
|
|
QCOMPARE(completionCount, completionCount);
|
|
QCOMPARE(completionModel->hasChildren(), hasChildren);
|
|
QCOMPARE(completionModel->columnCount(), columnCount);
|
|
}
|
|
|
|
void tst_QCompleter::sortedEngineRowCount_data()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
|
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
|
setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL);
|
|
|
|
QTest::addColumn<QString>("filterText");
|
|
QTest::addColumn<bool>("hasChildren");
|
|
QTest::addColumn<int>("rowCount");
|
|
QTest::addColumn<int>("completionCount");
|
|
|
|
QTest::newRow("whatever") << "whatever" << false << 0 << 0;
|
|
QTest::newRow("p") << "p" << true << 10 << 10;
|
|
QTest::newRow("p1") << "p1" << true << 2 << 2;
|
|
QTest::newRow("P1,") << "P1," << true << 5 << 5;
|
|
QTest::newRow("P1,c") << "P1,c" << true << 5 << 5;
|
|
QTest::newRow("P1,cc") << "P1,cc" << false << 0 << 0;
|
|
|
|
QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << false << 0 << 0;
|
|
|
|
QTest::newRow("whatever(filter off)") << "whatever" << true << 10 << 0;
|
|
QTest::newRow("p1(filter off)") << "p1" << true << 10 << 2;
|
|
QTest::newRow("p1,(filter off)") << "p1," << true << 5 << 5;
|
|
QTest::newRow("p1,c(filter off)") << "p1,c" << true << 5 << 5;
|
|
QTest::newRow("P1,cc(filter off)") << "P1,cc" << true << 5 << 0;
|
|
}
|
|
|
|
void tst_QCompleter::sortedEngineRowCount()
|
|
{
|
|
testRowCount();
|
|
}
|
|
|
|
void tst_QCompleter::unsortedEngineRowCount_data()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
|
completer->setCaseSensitivity(Qt::CaseSensitive);
|
|
setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL);
|
|
|
|
QTest::addColumn<QString>("filterText");
|
|
QTest::addColumn<bool>("hasChildren");
|
|
QTest::addColumn<int>("rowCount");
|
|
QTest::addColumn<int>("completionCount");
|
|
|
|
QTest::newRow("whatever") << "whatever" << false << 0 << 0;
|
|
QTest::newRow("p") << "p" << true << 5 << 5;
|
|
QTest::newRow("p1") << "p1" << true << 1 << 1;
|
|
QTest::newRow("P1,") << "P1," << true << 5 << 5;
|
|
QTest::newRow("P1,c") << "P1,c" << true << 5 << 5;
|
|
QTest::newRow("P1,cc") << "P1,cc" << false << 0 << 0;
|
|
|
|
QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << false << 0 << 0;
|
|
|
|
QTest::newRow("whatever(filter off)") << "whatever" << true << 10 << 0;
|
|
QTest::newRow("p1(filter off)") << "p1" << true << 10 << 1;
|
|
QTest::newRow("p1,(filter off)") << "p1," << true << 5 << 5;
|
|
QTest::newRow("p1,c(filter off)") << "p1,c" << true << 5 << 5;
|
|
QTest::newRow("P1,cc(filter off)") << "P1,cc" << true << 5 << 0;
|
|
}
|
|
|
|
void tst_QCompleter::unsortedEngineRowCount()
|
|
{
|
|
testRowCount();
|
|
}
|
|
|
|
void tst_QCompleter::currentRow()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
|
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
|
setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL);
|
|
|
|
// blank text
|
|
completer->setCompletionPrefix("");
|
|
QCOMPARE(completer->currentRow(), 0);
|
|
QVERIFY(completer->setCurrentRow(4));
|
|
QCOMPARE(completer->currentRow(), 4);
|
|
QVERIFY(!completer->setCurrentRow(13));
|
|
QVERIFY(completer->setCurrentRow(4));
|
|
|
|
// some text
|
|
completer->setCompletionPrefix("p1");
|
|
QCOMPARE(completer->currentRow(), 0);
|
|
QVERIFY(completer->setCurrentRow(1));
|
|
QCOMPARE(completer->currentRow(), 1);
|
|
QVERIFY(!completer->setCurrentRow(2));
|
|
QCOMPARE(completer->currentRow(), 1);
|
|
|
|
// invalid text
|
|
completer->setCompletionPrefix("well");
|
|
QCOMPARE(completer->currentRow(), -1);
|
|
}
|
|
|
|
void tst_QCompleter::sortedEngineMapFromSource()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
|
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
|
setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL);
|
|
|
|
QModelIndex si1, si2, pi;
|
|
QAbstractItemModel *sourceModel = completer->model();
|
|
auto completionModel = qobject_cast<const QAbstractProxyModel *>(completer->completionModel());
|
|
|
|
// Fitering ON
|
|
// empty
|
|
si1 = sourceModel->index(4, completionColumn); // "P2"
|
|
si2 = sourceModel->index(2, 0, si1); // "P2,c0P2"
|
|
pi = completionModel->mapFromSource(si1);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2"));
|
|
pi = completionModel->mapFromSource(si2);
|
|
QCOMPARE(pi.isValid(), false);
|
|
|
|
// some text
|
|
completer->setCompletionPrefix("p");
|
|
pi = completionModel->mapFromSource(si1);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2"));
|
|
pi = completionModel->mapFromSource(si2);
|
|
QCOMPARE(pi.isValid(), false);
|
|
|
|
// more text
|
|
completer->setCompletionPrefix("p2");
|
|
pi = completionModel->mapFromSource(si1);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2"));
|
|
pi = completionModel->mapFromSource(si2);
|
|
QCOMPARE(pi.isValid(), false);
|
|
|
|
// invalid text
|
|
completer->setCompletionPrefix("whatever");
|
|
pi = completionModel->mapFromSource(si1);
|
|
QVERIFY(!pi.isValid());
|
|
|
|
// Fitering OFF
|
|
completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
|
|
// empty
|
|
si1 = sourceModel->index(4, completionColumn); // "P2"
|
|
si2 = sourceModel->index(2, 0, si1); // "P2,c0P2"
|
|
pi = completionModel->mapFromSource(si1);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2"));
|
|
pi = completionModel->mapFromSource(si2);
|
|
QCOMPARE(pi.isValid(), false);
|
|
|
|
// some text
|
|
completer->setCompletionPrefix("p");
|
|
pi = completionModel->mapFromSource(si1);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2"));
|
|
pi = completionModel->mapFromSource(si2);
|
|
QCOMPARE(pi.isValid(), false);
|
|
|
|
// more text
|
|
completer->setCompletionPrefix("p2");
|
|
pi = completionModel->mapFromSource(si1);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2"));
|
|
pi = completionModel->mapFromSource(si2);
|
|
QCOMPARE(pi.isValid(), false);
|
|
|
|
// invalid text
|
|
completer->setCompletionPrefix("whatever");
|
|
pi = completionModel->mapFromSource(si1);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2"));
|
|
}
|
|
|
|
void tst_QCompleter::unsortedEngineMapFromSource()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
|
setSourceModel(HISTORY_MODEL); // case insensitively sorted model
|
|
completer->setModelSorting(QCompleter::UnsortedModel);
|
|
|
|
QModelIndex si, si2, si3, pi;
|
|
QAbstractItemModel *sourceModel = completer->model();
|
|
auto completionModel = qobject_cast<const QAbstractProxyModel *>(completer->completionModel());
|
|
|
|
si = sourceModel->index(6, completionColumn); // "P3"
|
|
QCOMPARE(si.data().toString(), QLatin1String("P3"));
|
|
si2 = sourceModel->index(3, completionColumn, sourceModel->index(0, completionColumn)); // "P0,c3P0"
|
|
QCOMPARE(si2.data().toString(), QLatin1String("c3P0"));
|
|
si3 = sourceModel->index(10, completionColumn); // "p3,c3p3" (history)
|
|
QCOMPARE(si3.data().toString(), QLatin1String("p3,c3p3"));
|
|
|
|
// FILTERING ON
|
|
// empty
|
|
pi = completionModel->mapFromSource(si);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3"));
|
|
pi = completionModel->mapFromSource(si2);
|
|
QCOMPARE(pi.isValid(), false);
|
|
pi = completionModel->mapFromSource(si3);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("p3,c3p3"));
|
|
|
|
// some text
|
|
completer->setCompletionPrefix("P");
|
|
pi = completionModel->mapFromSource(si);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3"));
|
|
pi = completionModel->mapFromSource(si2);
|
|
QCOMPARE(pi.isValid(), false);
|
|
pi = completionModel->mapFromSource(si3);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("p3,c3p3"));
|
|
|
|
// invalid text
|
|
completer->setCompletionPrefix("whatever");
|
|
pi = completionModel->mapFromSource(si);
|
|
QVERIFY(!pi.isValid());
|
|
pi = completionModel->mapFromSource(si2);
|
|
QVERIFY(!pi.isValid());
|
|
|
|
// tree matching
|
|
completer->setCompletionPrefix("P0,c");
|
|
pi = completionModel->mapFromSource(si);
|
|
QVERIFY(!pi.isValid());
|
|
pi = completionModel->mapFromSource(si2);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("c3P0"));
|
|
pi = completionModel->mapFromSource(si3);
|
|
QCOMPARE(pi.isValid(), false);
|
|
|
|
// more tree matching
|
|
completer->setCompletionPrefix("p3,");
|
|
pi = completionModel->mapFromSource(si2);
|
|
QVERIFY(!pi.isValid());
|
|
pi = completionModel->mapFromSource(si3);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("p3,c3p3"));
|
|
|
|
// FILTERING OFF
|
|
// empty
|
|
completer->setCompletionPrefix("");
|
|
completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
|
|
pi = completionModel->mapFromSource(si);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3"));
|
|
|
|
// some text
|
|
completer->setCompletionPrefix("P");
|
|
pi = completionModel->mapFromSource(si);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3"));
|
|
|
|
// more text
|
|
completer->setCompletionPrefix("P3");
|
|
pi = completionModel->mapFromSource(si);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3"));
|
|
|
|
// invalid text
|
|
completer->setCompletionPrefix("whatever");
|
|
pi = completionModel->mapFromSource(si);
|
|
QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3"));
|
|
}
|
|
|
|
void tst_QCompleter::historySearch()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
|
completer->setCaseSensitivity(Qt::CaseSensitive);
|
|
setSourceModel(HISTORY_MODEL);
|
|
|
|
auto completionModel = qobject_cast<const QAbstractProxyModel *>(completer->completionModel());
|
|
|
|
// "p3,c3p3" and "p2,c4p2" are added in the tree root
|
|
|
|
// FILTERING ON
|
|
// empty
|
|
completer->setCurrentRow(10);
|
|
QCOMPARE(completer->currentCompletion(), QLatin1String("p3,c3p3"));
|
|
|
|
// more text
|
|
completer->setCompletionPrefix("p2");
|
|
completer->setCurrentRow(1);
|
|
QCOMPARE(completer->currentCompletion(), QLatin1String("p2,c4p2"));
|
|
|
|
// comma separated text
|
|
completer->setCompletionPrefix("p2,c4");
|
|
completer->setCurrentRow(1);
|
|
QCOMPARE(completionModel->rowCount(), 2);
|
|
QCOMPARE(completer->currentCompletion(), QLatin1String("p2,c4p2"));
|
|
|
|
// invalid text
|
|
completer->setCompletionPrefix("whatever");
|
|
QCOMPARE(completer->currentCompletion(), QString());
|
|
|
|
// FILTERING OFF
|
|
completer->setCompletionPrefix("");
|
|
completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
|
|
completer->setCurrentRow(10);
|
|
QCOMPARE(completer->currentCompletion(), QLatin1String("p3,c3p3"));
|
|
|
|
// more text
|
|
completer->setCompletionPrefix("p2");
|
|
completer->setCurrentRow(1);
|
|
QCOMPARE(completer->currentCompletion(), QLatin1String("p2,c4p2"));
|
|
|
|
// comma separated text
|
|
completer->setCompletionPrefix("p2,c4");
|
|
QCOMPARE(completionModel->rowCount(), 5);
|
|
|
|
// invalid text
|
|
completer->setCompletionPrefix("whatever");
|
|
QCOMPARE(completer->currentCompletion(), QString());
|
|
}
|
|
|
|
void tst_QCompleter::setters()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
QVERIFY(completer->popup() != nullptr);
|
|
QPointer<QStandardItemModel> itemModel(new QStandardItemModel(1, 0, completer));
|
|
QAbstractItemModel *oldModel = completer->model();
|
|
completer->setModel(itemModel.data());
|
|
QVERIFY(completer->popup()->model() != oldModel);
|
|
QCOMPARE(completer->popup()->model(), completer->completionModel());
|
|
completer->setPopup(new QListView);
|
|
QCOMPARE(completer->popup()->model(), completer->completionModel());
|
|
completer->setModel(new QStringListModel(completer));
|
|
QVERIFY(itemModel.isNull()); // must have been deleted
|
|
|
|
completer->setModel(nullptr);
|
|
completer->setWidget(nullptr);
|
|
}
|
|
|
|
void tst_QCompleter::modelDeletion()
|
|
{
|
|
delete completer;
|
|
completer = new CsvCompleter;
|
|
const QStringList list = {"item1", "item2", "item3"};
|
|
std::unique_ptr<QStringListModel> listModel(new QStringListModel(list));
|
|
completer->setCompletionPrefix("i");
|
|
completer->setModel(listModel.get());
|
|
QCOMPARE(completer->completionCount(), 3);
|
|
std::unique_ptr<QListView> view(new QListView);
|
|
view->setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
|
view->setModel(completer->completionModel());
|
|
listModel.reset();
|
|
view->move(200, 200);
|
|
view->show();
|
|
QCoreApplication::processEvents();
|
|
view.reset();
|
|
QCOMPARE(completer->completionCount(), 0);
|
|
QCOMPARE(completer->currentRow(), -1);
|
|
}
|
|
|
|
void tst_QCompleter::multipleWidgets()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
QStringList list;
|
|
list << "item1" << "item2" << "item2";
|
|
QCompleter completer(list);
|
|
completer.setCompletionMode(QCompleter::InlineCompletion);
|
|
|
|
QWidget window;
|
|
window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
|
window.move(200, 200);
|
|
window.show();
|
|
QApplicationPrivate::setActiveWindow(&window);
|
|
QVERIFY(QTest::qWaitForWindowActive(&window));
|
|
|
|
QFocusEvent focusIn(QEvent::FocusIn);
|
|
QFocusEvent focusOut(QEvent::FocusOut);
|
|
|
|
auto comboBox = new QComboBox(&window);
|
|
comboBox->setEditable(true);
|
|
comboBox->setCompleter(&completer);
|
|
comboBox->setFocus();
|
|
comboBox->show();
|
|
window.activateWindow();
|
|
QApplicationPrivate::setActiveWindow(&window);
|
|
QVERIFY(QTest::qWaitForWindowActive(&window));
|
|
QCOMPARE(QApplication::focusWidget(), comboBox);
|
|
comboBox->lineEdit()->setText("it");
|
|
QCOMPARE(comboBox->currentText(), QString("it")); // should not complete with setText
|
|
QTest::keyPress(comboBox, 'e');
|
|
QCOMPARE(comboBox->currentText(), QString("item1"));
|
|
comboBox->clearEditText();
|
|
QCOMPARE(comboBox->currentText(), QString("")); // combo box text must not change!
|
|
|
|
auto lineEdit = new QLineEdit(&window);
|
|
lineEdit->setCompleter(&completer);
|
|
lineEdit->show();
|
|
lineEdit->setFocus();
|
|
QTRY_COMPARE(QApplication::focusWidget(), lineEdit);
|
|
lineEdit->setText("it");
|
|
QCOMPARE(lineEdit->text(), QString("it")); // should not completer with setText
|
|
QCOMPARE(comboBox->currentText(), QString("")); // combo box text must not change!
|
|
QTest::keyPress(lineEdit, 'e');
|
|
QCOMPARE(lineEdit->text(), QString("item1"));
|
|
QCOMPARE(comboBox->currentText(), QString("")); // combo box text must not change!
|
|
}
|
|
|
|
void tst_QCompleter::focusIn()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
QCompleter completer({"item1", "item2", "item2"});
|
|
|
|
QWidget window;
|
|
window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
|
window.move(200, 200);
|
|
window.show();
|
|
window.activateWindow();
|
|
QApplicationPrivate::setActiveWindow(&window);
|
|
QVERIFY(QTest::qWaitForWindowActive(&window));
|
|
|
|
auto comboBox = new QComboBox(&window);
|
|
comboBox->setEditable(true);
|
|
comboBox->setCompleter(&completer);
|
|
comboBox->show();
|
|
comboBox->lineEdit()->setText("it");
|
|
|
|
auto lineEdit = new QLineEdit(&window);
|
|
lineEdit->setCompleter(&completer);
|
|
lineEdit->setText("it");
|
|
lineEdit->show();
|
|
|
|
auto lineEdit2 = new QLineEdit(&window); // has no completer!
|
|
lineEdit2->show();
|
|
|
|
comboBox->setFocus();
|
|
QTRY_COMPARE(completer.widget(), comboBox);
|
|
lineEdit->setFocus();
|
|
QTRY_COMPARE(completer.widget(), lineEdit);
|
|
comboBox->setFocus();
|
|
QTRY_COMPARE(completer.widget(), comboBox);
|
|
lineEdit2->setFocus();
|
|
QTRY_COMPARE(completer.widget(), comboBox);
|
|
}
|
|
|
|
void tst_QCompleter::dynamicSortOrder()
|
|
{
|
|
QStandardItemModel model;
|
|
QCompleter completer(&model);
|
|
completer.setModelSorting(QCompleter::CaseSensitivelySortedModel);
|
|
QStandardItem *root = model.invisibleRootItem();
|
|
for (int i = 0; i < 20; i++) {
|
|
root->appendRow(new QStandardItem(QString::number(i)));
|
|
}
|
|
root->appendRow(new QStandardItem("13"));
|
|
root->sortChildren(0, Qt::AscendingOrder);
|
|
completer.setCompletionPrefix("1");
|
|
QCOMPARE(completer.completionCount(), 12);
|
|
completer.setCompletionPrefix("13");
|
|
QCOMPARE(completer.completionCount(), 2);
|
|
|
|
root->sortChildren(0, Qt::DescendingOrder);
|
|
completer.setCompletionPrefix("13");
|
|
QCOMPARE(completer.completionCount(), 2);
|
|
completer.setCompletionPrefix("1");
|
|
QCOMPARE(completer.completionCount(), 12);
|
|
}
|
|
|
|
void tst_QCompleter::disabledItems()
|
|
{
|
|
QLineEdit lineEdit;
|
|
lineEdit.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
|
auto model = new QStandardItemModel(&lineEdit);
|
|
QStandardItem *suggestions = new QStandardItem("suggestions");
|
|
suggestions->setEnabled(false);
|
|
model->appendRow(suggestions);
|
|
model->appendRow(new QStandardItem("suggestions Enabled"));
|
|
auto completer = new QCompleter(model, &lineEdit);
|
|
QSignalSpy spy(completer, QOverload<const QString &>::of(&QCompleter::activated));
|
|
lineEdit.setCompleter(completer);
|
|
lineEdit.move(200, 200);
|
|
lineEdit.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&lineEdit));
|
|
|
|
QTest::keyPress(&lineEdit, Qt::Key_S);
|
|
QTest::keyPress(&lineEdit, Qt::Key_U);
|
|
QAbstractItemView *view = lineEdit.completer()->popup();
|
|
QVERIFY(view->isVisible());
|
|
QTest::mouseClick(view->viewport(), Qt::LeftButton, {}, view->visualRect(view->model()->index(0, 0)).center());
|
|
QCOMPARE(spy.size(), 0);
|
|
QVERIFY(view->isVisible());
|
|
QTest::mouseClick(view->viewport(), Qt::LeftButton, {}, view->visualRect(view->model()->index(1, 0)).center());
|
|
QCOMPARE(spy.size(), 1);
|
|
QVERIFY(!view->isVisible());
|
|
}
|
|
|
|
void tst_QCompleter::task178797_activatedOnReturn()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
QLineEdit ledit;
|
|
setFrameless(&ledit);
|
|
auto completer = new QCompleter({"foobar1", "foobar2"}, &ledit);
|
|
ledit.setCompleter(completer);
|
|
QSignalSpy spy(completer, QOverload<const QString &>::of(&QCompleter::activated));
|
|
QCOMPARE(spy.size(), 0);
|
|
ledit.move(200, 200);
|
|
ledit.show();
|
|
QApplicationPrivate::setActiveWindow(&ledit);
|
|
QVERIFY(QTest::qWaitForWindowActive(&ledit));
|
|
QTest::keyClick(&ledit, Qt::Key_F);
|
|
QCoreApplication::processEvents();
|
|
QTRY_VERIFY(QApplication::activePopupWidget());
|
|
QTest::keyClick(QApplication::activePopupWidget(), Qt::Key_Down);
|
|
QCoreApplication::processEvents();
|
|
QTest::keyClick(QApplication::activePopupWidget(), Qt::Key_Return);
|
|
QCoreApplication::processEvents();
|
|
QCOMPARE(spy.size(), 1);
|
|
}
|
|
|
|
class task189564_StringListModel : public QStringListModel
|
|
{
|
|
const QString omitString;
|
|
Qt::ItemFlags flags(const QModelIndex &index) const override
|
|
{
|
|
Qt::ItemFlags flags = Qt::ItemIsEnabled;
|
|
if (data(index, Qt::DisplayRole).toString() != omitString)
|
|
flags |= Qt::ItemIsSelectable;
|
|
return flags;
|
|
}
|
|
public:
|
|
explicit task189564_StringListModel(const QString &omitString, QObject *parent = nullptr)
|
|
: QStringListModel(parent)
|
|
, omitString(omitString)
|
|
{
|
|
}
|
|
};
|
|
|
|
void tst_QCompleter::task189564_omitNonSelectableItems()
|
|
{
|
|
const QString prefix("a");
|
|
const int n = 5;
|
|
|
|
QStringList strings;
|
|
for (int i = 0; i < n; ++i)
|
|
strings << prefix + QString::number(i);
|
|
const QString omitString(strings.at(n / 2));
|
|
task189564_StringListModel model(omitString);
|
|
model.setStringList(strings);
|
|
QCompleter completer_(&model);
|
|
completer_.setCompletionPrefix(prefix);
|
|
|
|
QAbstractItemModel *completionModel = completer_.completionModel();
|
|
QModelIndexList matches1 =
|
|
completionModel->match(completionModel->index(0, 0), Qt::DisplayRole, prefix, -1);
|
|
QCOMPARE(matches1.size(), n - 1);
|
|
QModelIndexList matches2 =
|
|
completionModel->match(completionModel->index(0, 0), Qt::DisplayRole, omitString);
|
|
QVERIFY(matches2.isEmpty());
|
|
}
|
|
|
|
class task246056_ComboBox : public QComboBox
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
task246056_ComboBox()
|
|
{
|
|
setEditable(true);
|
|
setInsertPolicy(NoInsert);
|
|
if (completer()) {
|
|
completer()->setCompletionMode(QCompleter::PopupCompletion);
|
|
completer()->setCompletionRole(Qt::DisplayRole);
|
|
connect(lineEdit(), &QLineEdit::editingFinished, this, &task246056_ComboBox::setCompletionPrefix);
|
|
}
|
|
}
|
|
private slots:
|
|
void setCompletionPrefix() { completer()->setCompletionPrefix(lineEdit()->text()); }
|
|
};
|
|
|
|
void tst_QCompleter::task246056_setCompletionPrefix()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
task246056_ComboBox comboBox;
|
|
setFrameless(&comboBox);
|
|
QVERIFY(comboBox.completer());
|
|
comboBox.addItem("");
|
|
comboBox.addItem("a1");
|
|
comboBox.addItem("a2");
|
|
comboBox.move(200, 200);
|
|
comboBox.show();
|
|
QApplicationPrivate::setActiveWindow(&comboBox);
|
|
QVERIFY(QTest::qWaitForWindowActive(&comboBox));
|
|
QSignalSpy spy(comboBox.completer(), QOverload<const QModelIndex &>::of(&QCompleter::activated));
|
|
QTest::keyPress(&comboBox, 'a');
|
|
QTest::keyPress(comboBox.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyPress(comboBox.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyPress(comboBox.completer()->popup(), Qt::Key_Enter); // don't crash!
|
|
QCOMPARE(spy.size(), 1);
|
|
const auto index = spy.at(0).constFirst().toModelIndex();
|
|
QVERIFY(!index.isValid());
|
|
}
|
|
|
|
class task250064_TextEdit : public QTextEdit
|
|
{
|
|
public:
|
|
QCompleter *completer;
|
|
|
|
task250064_TextEdit()
|
|
{
|
|
completer = new QCompleter(this);
|
|
completer->setWidget(this);
|
|
}
|
|
|
|
void keyPressEvent (QKeyEvent *e) override
|
|
{
|
|
completer->popup();
|
|
QTextEdit::keyPressEvent(e);
|
|
}
|
|
};
|
|
|
|
class task250064_Widget : public QWidget
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
task250064_Widget() : m_textEdit(new task250064_TextEdit)
|
|
{
|
|
auto tabWidget = new QTabWidget;
|
|
tabWidget->setFocusPolicy(Qt::ClickFocus);
|
|
tabWidget->addTab(m_textEdit, "untitled");
|
|
|
|
auto layout = new QVBoxLayout(this);
|
|
layout->addWidget(tabWidget);
|
|
|
|
m_textEdit->setPlainText("bla bla bla");
|
|
m_textEdit->setFocus();
|
|
}
|
|
|
|
void setCompletionModel()
|
|
{
|
|
m_textEdit->completer->setModel(nullptr);
|
|
}
|
|
|
|
QTextEdit *textEdit() const { return m_textEdit; }
|
|
|
|
private:
|
|
task250064_TextEdit *m_textEdit;
|
|
};
|
|
|
|
void tst_QCompleter::task250064_lostFocus()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
task250064_Widget widget;
|
|
widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
|
widget.show();
|
|
QApplicationPrivate::setActiveWindow(&widget);
|
|
QVERIFY(QTest::qWaitForWindowActive(&widget));
|
|
QTest::keyPress(widget.textEdit(), 'a');
|
|
Qt::FocusPolicy origPolicy = widget.textEdit()->focusPolicy();
|
|
QVERIFY(origPolicy != Qt::NoFocus);
|
|
widget.setCompletionModel();
|
|
QCOMPARE(widget.textEdit()->focusPolicy(), origPolicy);
|
|
}
|
|
|
|
void tst_QCompleter::task253125_lineEditCompletion_data()
|
|
{
|
|
QTest::addColumn<QStringList>("list");
|
|
QTest::addColumn<QCompleter::CompletionMode>("completionMode");
|
|
|
|
QStringList list = {"alpha", "beta", "gamma", "delta", "epsilon", "zeta",
|
|
"eta", "theta", "iota", "kappa", "lambda", "mu",
|
|
"nu", "xi", "omicron", "pi", "rho", "sigma",
|
|
"tau", "upsilon", "phi", "chi", "psi", "omega"};
|
|
|
|
QTest::newRow("Inline") << list << QCompleter::InlineCompletion;
|
|
QTest::newRow("Filtered") << list << QCompleter::PopupCompletion;
|
|
QTest::newRow("Unfiltered") << list << QCompleter::UnfilteredPopupCompletion;
|
|
}
|
|
|
|
void tst_QCompleter::task253125_lineEditCompletion()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
QFETCH(QStringList, list);
|
|
QFETCH(QCompleter::CompletionMode, completionMode);
|
|
|
|
std::unique_ptr<QStringListModel> model(new QStringListModel(list));
|
|
|
|
std::unique_ptr<QCompleter> completer(new QCompleter(list));
|
|
completer->setModel(model.get());
|
|
completer->setCompletionMode(completionMode);
|
|
|
|
QLineEdit edit;
|
|
edit.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::")
|
|
+ QLatin1String(QTest::currentDataTag()));
|
|
edit.setCompleter(completer.get());
|
|
edit.move(200, 200);
|
|
edit.show();
|
|
edit.setFocus();
|
|
QApplicationPrivate::setActiveWindow(&edit);
|
|
QVERIFY(QTest::qWaitForWindowActive(&edit));
|
|
|
|
QTest::keyClick(&edit, 'i');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString("iota"));
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("iota"));
|
|
|
|
edit.clear();
|
|
completer->setCompletionMode(QCompleter::PopupCompletion);
|
|
completer->setFilterMode(Qt::MatchContains);
|
|
|
|
QTest::keyClick(&edit, 't');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString("beta"));
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("beta"));
|
|
|
|
edit.clear();
|
|
|
|
QTest::keyClick(&edit, 'p');
|
|
QTest::keyClick(&edit, 'p');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString("kappa"));
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("kappa"));
|
|
|
|
edit.clear();
|
|
completer->setFilterMode(Qt::MatchStartsWith);
|
|
|
|
QTest::keyClick(&edit, 't');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString("theta"));
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("theta"));
|
|
|
|
edit.clear();
|
|
|
|
QTest::keyClick(&edit, 'p');
|
|
QTest::keyClick(&edit, 'p');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString());
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("pp"));
|
|
|
|
edit.clear();
|
|
|
|
QTest::keyClick(&edit, 'u');
|
|
QTest::keyClick(&edit, 'p');
|
|
QTest::keyClick(&edit, 's');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString("upsilon"));
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("upsilon"));
|
|
|
|
edit.clear();
|
|
completer->setFilterMode(Qt::MatchEndsWith);
|
|
|
|
QTest::keyClick(&edit, 'm');
|
|
QTest::keyClick(&edit, 'm');
|
|
QTest::keyClick(&edit, 'a');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString("gamma"));
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("gamma"));
|
|
|
|
edit.clear();
|
|
|
|
QTest::keyClick(&edit, 'g');
|
|
QTest::keyClick(&edit, 'm');
|
|
QTest::keyClick(&edit, 'a');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString("sigma"));
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("sigma"));
|
|
|
|
edit.clear();
|
|
|
|
QTest::keyClick(&edit, 'm');
|
|
QTest::keyClick(&edit, 'm');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString());
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("mm"));
|
|
|
|
edit.clear();
|
|
completer->setFilterMode(Qt::MatchStartsWith);
|
|
|
|
QTest::keyClick(&edit, 'z');
|
|
QTest::keyClick(&edit, 'e');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString("zeta"));
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("zeta"));
|
|
|
|
edit.clear();
|
|
completer->setFilterMode(Qt::MatchEndsWith);
|
|
|
|
QTest::keyClick(&edit, 'e');
|
|
QTest::keyClick(&edit, 'g');
|
|
QTest::keyClick(&edit, 'a');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString("omega"));
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("omega"));
|
|
|
|
edit.clear();
|
|
completer->setFilterMode(Qt::MatchContains);
|
|
|
|
QTest::keyClick(&edit, 'c');
|
|
QTest::keyClick(&edit, 'r');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString("omicron"));
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("omicron"));
|
|
|
|
edit.clear();
|
|
|
|
QTest::keyClick(&edit, 'z');
|
|
QTest::keyClick(&edit, 'z');
|
|
QCOMPARE(edit.completer()->currentCompletion(), QString());
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("zz"));
|
|
}
|
|
|
|
void tst_QCompleter::task247560_keyboardNavigation()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
QStandardItemModel model;
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
const QString prefix = QLatin1String("row ") + QString::number(i) + QLatin1String(" column ");
|
|
for (int j = 0; j < 5; j++)
|
|
model.setItem(i, j, new QStandardItem(prefix + QString::number(j)));
|
|
}
|
|
|
|
|
|
QCompleter completer(&model);
|
|
completer.setCompletionColumn(1);
|
|
|
|
QLineEdit edit;
|
|
edit.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
|
edit.setCompleter(&completer);
|
|
edit.move(200, 200);
|
|
edit.show();
|
|
edit.setFocus();
|
|
QApplicationPrivate::setActiveWindow(&edit);
|
|
QVERIFY(QTest::qWaitForWindowActive(&edit));
|
|
|
|
QTest::keyClick(&edit, 'r');
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("row 1 column 1"));
|
|
|
|
edit.clear();
|
|
|
|
QTest::keyClick(&edit, 'r');
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Up);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Up);
|
|
QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter);
|
|
|
|
QCOMPARE(edit.text(), QString("row 3 column 1"));
|
|
}
|
|
|
|
// Helpers for QTBUG_14292_filesystem: Recursion helper for below recurseTreeModel
|
|
// Function to recurse over a tree model applying a function
|
|
// taking index and depth, returning true to terminate recursion.
|
|
template <class Function>
|
|
bool recurseTreeModel(const QAbstractItemModel &m, const QModelIndex &idx, Function f, int depth = 0)
|
|
{
|
|
if (idx.isValid() && f(idx, depth))
|
|
return true;
|
|
const int rowCount = m.rowCount(idx);
|
|
for (int row = 0; row < rowCount; ++row)
|
|
if (recurseTreeModel(m, m.index(row, 0, idx), f, depth + 1))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// Function applicable to the above recurseTreeModel() to search for a data item.
|
|
class SearchFunction
|
|
{
|
|
public:
|
|
SearchFunction(const QString &needle, int role = Qt::DisplayRole) :
|
|
m_needle(needle), m_role(role) {}
|
|
|
|
bool operator()(const QModelIndex &idx, int /* depth */) const
|
|
{ return idx.data(m_role).toString() == m_needle; }
|
|
|
|
private:
|
|
const QString m_needle;
|
|
const int m_role;
|
|
};
|
|
|
|
// Function applicable to the above recurseTreeModel() for debug output
|
|
// of a model.
|
|
class DebugFunction
|
|
{
|
|
public:
|
|
DebugFunction(QDebug d) : m_d(d) {}
|
|
|
|
bool operator()(const QModelIndex &idx, int depth)
|
|
{
|
|
for (int i = 0; i < 4 * depth; ++i)
|
|
m_d << ' ';
|
|
m_d << idx.data(QFileSystemModel::FileNameRole).toString()
|
|
<< '\n';
|
|
return false;
|
|
}
|
|
private:
|
|
QDebug m_d;
|
|
};
|
|
|
|
QDebug operator<<(QDebug d, const QAbstractItemModel &m)
|
|
{
|
|
QDebug dns = d.nospace();
|
|
dns << '\n';
|
|
recurseTreeModel(m, QModelIndex(), DebugFunction(dns));
|
|
return d;
|
|
}
|
|
|
|
static const char testDir1[] = "hello";
|
|
static const char testDir2[] = "holla";
|
|
|
|
// Helper for QTBUG_14292_filesystem, checking whether both
|
|
// test directories are seen by the file system model for usage
|
|
// with QTRY_VERIFY.
|
|
|
|
static inline bool testFileSystemReady(const QAbstractItemModel &model)
|
|
{
|
|
return recurseTreeModel(model, QModelIndex(), SearchFunction(QLatin1String(testDir1), QFileSystemModel::FileNameRole))
|
|
&& recurseTreeModel(model, QModelIndex(), SearchFunction(QLatin1String(testDir2), QFileSystemModel::FileNameRole));
|
|
}
|
|
|
|
void tst_QCompleter::QTBUG_14292_filesystem()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
// This test tests whether the creation of subdirectories
|
|
// does not cause completers based on file system models
|
|
// to pop up the completion list due to file changed signals.
|
|
FileSystem fs;
|
|
QFileSystemModel model;
|
|
QSignalSpy filesAddedSpy(&model, &QAbstractItemModel::rowsInserted);
|
|
model.setRootPath(fs.path());
|
|
|
|
QVERIFY(fs.createDirectory(QLatin1String(testDir1)));
|
|
QVERIFY(fs.createDirectory(QLatin1String(testDir2)));
|
|
|
|
QLineEdit edit;
|
|
edit.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
|
QCompleter comp;
|
|
comp.setModel(&model);
|
|
edit.setCompleter(&comp);
|
|
|
|
edit.move(200, 200);
|
|
edit.show();
|
|
QApplicationPrivate::setActiveWindow(&edit);
|
|
QVERIFY(QTest::qWaitForWindowActive(&edit));
|
|
QCOMPARE(QApplication::activeWindow(), &edit);
|
|
edit.setFocus();
|
|
QTRY_VERIFY(edit.hasFocus());
|
|
|
|
// Wait for all file system model slots/timers to trigger
|
|
// until the model sees the subdirectories.
|
|
QTRY_VERIFY(testFileSystemReady(model));
|
|
// But this should not cause the combo to pop up.
|
|
QVERIFY(!comp.popup()->isVisible());
|
|
edit.setText(fs.path());
|
|
QTest::keyClick(&edit, '/');
|
|
QTRY_VERIFY(comp.popup()->isVisible());
|
|
QCOMPARE(comp.popup()->model()->rowCount(), 2);
|
|
QApplication::processEvents();
|
|
QTest::keyClick(&edit, 'h');
|
|
QCOMPARE(comp.popup()->model()->rowCount(), 2);
|
|
QTest::keyClick(&edit, 'e');
|
|
QCOMPARE(comp.popup()->model()->rowCount(), 1);
|
|
QTest::keyClick(&edit, 'r');
|
|
QTRY_VERIFY(!comp.popup()->isVisible());
|
|
QVERIFY(fs.createDirectory(QStringLiteral("hero")));
|
|
if (!filesAddedSpy.wait())
|
|
QSKIP("File system model didn't notify about new directory, skipping tests");
|
|
QTRY_VERIFY(comp.popup()->isVisible());
|
|
QCOMPARE(comp.popup()->model()->rowCount(), 1);
|
|
QTest::keyClick(comp.popup(), Qt::Key_Escape);
|
|
QTRY_VERIFY(!comp.popup()->isVisible());
|
|
QVERIFY(fs.createDirectory(QStringLiteral("nothingThere")));
|
|
//there is no reason creating a file should open a popup, it did in Qt 4.7.0
|
|
if (!filesAddedSpy.wait())
|
|
QSKIP("File system model didn't notify about new file, skipping tests");
|
|
QVERIFY(!comp.popup()->isVisible());
|
|
|
|
QTest::keyClick(&edit, Qt::Key_Backspace);
|
|
QTRY_VERIFY(comp.popup()->isVisible());
|
|
QCOMPARE(comp.popup()->model()->rowCount(), 2);
|
|
QTest::keyClick(&edit, 'm');
|
|
QTRY_VERIFY(!comp.popup()->isVisible());
|
|
|
|
QWidget w;
|
|
w.move(400, 200);
|
|
w.show();
|
|
QApplicationPrivate::setActiveWindow(&w);
|
|
QVERIFY(QTest::qWaitForWindowActive(&w));
|
|
QVERIFY(!edit.hasFocus() && !comp.popup()->hasFocus());
|
|
|
|
QVERIFY(fs.createDirectory(QStringLiteral("hemo")));
|
|
//there is no reason creating a file should open a popup, it did in Qt 4.7.0
|
|
if (!filesAddedSpy.wait())
|
|
QSKIP("File system model didn't notify about new file, skipping tests");
|
|
QVERIFY(!comp.popup()->isVisible());
|
|
}
|
|
|
|
void tst_QCompleter::QTBUG_52028_tabAutoCompletes()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
QWidget w;
|
|
w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
|
w.setLayout(new QVBoxLayout);
|
|
|
|
QComboBox cbox;
|
|
cbox.setEditable(true);
|
|
cbox.setInsertPolicy(QComboBox::NoInsert);
|
|
cbox.addItems({"foobar1", "foobar2", "hux"});
|
|
|
|
cbox.completer()->setCaseSensitivity(Qt::CaseInsensitive);
|
|
cbox.completer()->setCompletionMode(QCompleter::PopupCompletion);
|
|
|
|
w.layout()->addWidget(&cbox);
|
|
|
|
// Adding a line edit is a good reason for tab to do something unrelated
|
|
auto le = new QLineEdit;
|
|
w.layout()->addWidget(le);
|
|
|
|
const auto pos = w.screen()->availableGeometry().topLeft() + QPoint(200,200);
|
|
w.move(pos);
|
|
w.show();
|
|
QApplicationPrivate::setActiveWindow(&w);
|
|
QVERIFY(QTest::qWaitForWindowActive(&w));
|
|
|
|
QSignalSpy activatedSpy(&cbox, &QComboBox::activated);
|
|
|
|
// Tab key will complete but not activate
|
|
cbox.lineEdit()->clear();
|
|
QTest::keyClick(&cbox, Qt::Key_H);
|
|
QVERIFY(cbox.completer()->popup());
|
|
QTRY_VERIFY(cbox.completer()->popup()->isVisible());
|
|
QTest::keyClick(cbox.completer()->popup(), Qt::Key_Tab);
|
|
QCOMPARE(cbox.completer()->currentCompletion(), QLatin1String("hux"));
|
|
QCOMPARE(activatedSpy.size(), 0);
|
|
QEXPECT_FAIL("", "QTBUG-52028 will not be fixed today.", Abort);
|
|
QCOMPARE(cbox.currentText(), QLatin1String("hux"));
|
|
QCOMPARE(activatedSpy.size(), 0);
|
|
QVERIFY(!le->hasFocus());
|
|
}
|
|
|
|
void tst_QCompleter::QTBUG_51889_activatedSentTwice()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: This fails. Figure out why.");
|
|
|
|
QWidget w;
|
|
w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
|
w.setLayout(new QVBoxLayout);
|
|
|
|
QComboBox cbox;
|
|
setFrameless(&cbox);
|
|
cbox.setEditable(true);
|
|
cbox.setInsertPolicy(QComboBox::NoInsert);
|
|
cbox.addItems({"foobar1", "foobar2", "bar", "hux"});
|
|
|
|
cbox.completer()->setCaseSensitivity(Qt::CaseInsensitive);
|
|
cbox.completer()->setCompletionMode(QCompleter::PopupCompletion);
|
|
|
|
w.layout()->addWidget(&cbox);
|
|
|
|
w.layout()->addWidget(new QLineEdit);
|
|
|
|
const auto pos = w.screen()->availableGeometry().topLeft() + QPoint(200,200);
|
|
w.move(pos);
|
|
w.show();
|
|
QApplicationPrivate::setActiveWindow(&w);
|
|
QVERIFY(QTest::qWaitForWindowActive(&w));
|
|
|
|
QSignalSpy activatedSpy(&cbox, &QComboBox::activated);
|
|
|
|
// Navigate + enter activates only once (first item)
|
|
cbox.lineEdit()->clear();
|
|
QTest::keyClick(&cbox, Qt::Key_F);
|
|
QVERIFY(cbox.completer()->popup());
|
|
QTRY_VERIFY(cbox.completer()->popup()->isVisible());
|
|
QTest::keyClick(cbox.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(cbox.completer()->popup(), Qt::Key_Return);
|
|
QTRY_COMPARE(activatedSpy.size(), 1);
|
|
|
|
// Navigate + enter activates only once (non-first item)
|
|
cbox.lineEdit()->clear();
|
|
activatedSpy.clear();
|
|
QTest::keyClick(&cbox, Qt::Key_H);
|
|
QVERIFY(cbox.completer()->popup());
|
|
QTRY_VERIFY(cbox.completer()->popup()->isVisible());
|
|
QTest::keyClick(cbox.completer()->popup(), Qt::Key_Down);
|
|
QTest::keyClick(cbox.completer()->popup(), Qt::Key_Return);
|
|
QTRY_COMPARE(activatedSpy.size(), 1);
|
|
|
|
// Full text + enter activates only once
|
|
cbox.lineEdit()->clear();
|
|
activatedSpy.clear();
|
|
QTest::keyClicks(&cbox, "foobar1");
|
|
QVERIFY(cbox.completer()->popup());
|
|
QTRY_VERIFY(cbox.completer()->popup()->isVisible());
|
|
QTest::keyClick(&cbox, Qt::Key_Return);
|
|
QTRY_COMPARE(activatedSpy.size(), 1);
|
|
}
|
|
|
|
void tst_QCompleter::showPopupInGraphicsView()
|
|
{
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
|
QSKIP("Wayland: Skip this test, see also QTBUG-107186");
|
|
|
|
QGraphicsView view;
|
|
QGraphicsScene scene;
|
|
view.setScene(&scene);
|
|
|
|
QLineEdit lineEdit;
|
|
lineEdit.setCompleter(new QCompleter({"alpha", "omega", "omicron", "zeta"}));
|
|
scene.addWidget(&lineEdit);
|
|
|
|
view.move(view.screen()->availableGeometry().topLeft() + QPoint(10, 10));
|
|
view.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
|
|
|
// show popup under line edit
|
|
QTest::keyClick(&lineEdit, Qt::Key_A);
|
|
QVERIFY(lineEdit.completer()->popup());
|
|
QVERIFY(lineEdit.completer()->popup()->isVisible());
|
|
QCOMPARE(lineEdit.completer()->popup()->geometry().x(), lineEdit.mapToGlobal(QPoint(0, 0)).x());
|
|
QVERIFY(lineEdit.completer()->popup()->geometry().top() >= (lineEdit.mapToGlobal(QPoint(0, lineEdit.height() - 1)).y() - 1));
|
|
|
|
// move widget to the bottom of screen
|
|
lineEdit.clear();
|
|
int y = view.screen()->availableGeometry().height() - lineEdit.geometry().y();
|
|
view.move(view.geometry().x(), y);
|
|
|
|
// show popup above line edit
|
|
QTest::keyClick(&lineEdit, Qt::Key_A);
|
|
QVERIFY(lineEdit.completer()->popup()->geometry().bottom() < lineEdit.mapToGlobal(QPoint(0, 0)).y());
|
|
}
|
|
|
|
void tst_QCompleter::inheritedEventFilter()
|
|
{
|
|
class Completer : public QCompleter
|
|
{
|
|
public:
|
|
explicit Completer(QWidget *parent) : QCompleter(parent)
|
|
{
|
|
Q_ASSERT(parent);
|
|
setPopup(new QListView());
|
|
popup()->installEventFilter(this);
|
|
}
|
|
|
|
bool m_popupChildAdded = false;
|
|
|
|
protected:
|
|
bool eventFilter(QObject *watched, QEvent *event) override
|
|
{
|
|
if (watched == popup() && event->type() == QEvent::ChildAdded)
|
|
m_popupChildAdded = true;
|
|
|
|
return QCompleter::eventFilter(watched, event);
|
|
}
|
|
};
|
|
|
|
QComboBox comboBox;
|
|
comboBox.setEditable(true);
|
|
Completer *completer = new Completer(&comboBox);
|
|
comboBox.setCompleter(completer);
|
|
|
|
// comboBox.show() must not crash with an infinite loop in the event filter
|
|
comboBox.show();
|
|
QVERIFY(QTest::qWaitForWindowExposed(&comboBox));
|
|
|
|
// Since event orders are platform dependent, only the the ChildAdded event is checked.
|
|
QVERIFY(QTest::qWaitFor([completer](){return completer->m_popupChildAdded; }));
|
|
}
|
|
|
|
QTEST_MAIN(tst_QCompleter)
|
|
#include "tst_qcompleter.moc"
|