|| !keyboardTimeWasValid || keys.isEmpty()) {
m_searchedString.clear();
}
- const bool searchFromNextItem = m_searchedString.isEmpty();
+
+ const bool newSearch = m_searchedString.isEmpty();
+
if (!keys.isEmpty()) {
m_searchedString.append(keys);
- emit changeCurrentItem(m_searchedString, searchFromNextItem);
+
+ // Special case:
+ // If the same key is pressed repeatedly, the next item matching that key should be highlighted
+ const QChar firstKey = m_searchedString.length() > 0 ? m_searchedString.at(0) : QChar();
+ const bool sameKey = m_searchedString.length() > 1 && m_searchedString.count(firstKey) == m_searchedString.length();
+
+ // Searching for a matching item should start from the next item if either
+ // 1. a new search is started, or
+ // 2. a 'repeated key' search is done.
+ const bool searchFromNextItem = newSearch || sameKey;
+
+ emit changeCurrentItem(sameKey ? firstKey : m_searchedString, searchFromNextItem);
}
m_keyboardInputTime.start();
}
#include <QString>
#include <QElapsedTimer>
-class KItemListController;
-class QInputMethodEvent;
-class QKeyEvent;
-
/**
* @brief Controls the keyboard searching ability for a KItemListController.
*
kde4_add_unit_test(kfileitemmodeltest TEST ${kfileitemmodeltest_SRCS})
target_link_libraries(kfileitemmodeltest dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
+# KItemListKeyboardSearchManagerTest
+set(kitemlistkeyboardsearchmanagertest_SRCS
+ kitemlistkeyboardsearchmanagertest.cpp
+ ../kitemviews/kitemlistkeyboardsearchmanager.cpp
+)
+kde4_add_unit_test(kitemlistkeyboardsearchmanagertest TEST ${kitemlistkeyboardsearchmanagertest_SRCS})
+target_link_libraries(kitemlistkeyboardsearchmanagertest ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
+
# DolphinSearchBox
if (Nepomuk_FOUND)
set(dolphinsearchboxtest_SRCS
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
+ * Copyright (C) 2011 by Frank Reininghaus <frank78ac@googlemail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
void testExpansionLevelsCompare_data();
void testExpansionLevelsCompare();
+ void testIndexForKeyboardSearch();
+
private:
bool isModelConsistent() const;
QCOMPARE(m_model->expansionLevelsCompare(a, b), result);
}
+void KFileItemModelTest::testIndexForKeyboardSearch()
+{
+ QStringList files;
+ files << "a" << "aa" << "Image.jpg" << "Image.png" << "Text" << "Text1" << "Text2" << "Text11";
+ m_testDir->createFiles(files);
+
+ m_dirLister->openUrl(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+
+ // Search from index 0
+ QCOMPARE(m_model->indexForKeyboardSearch("a", 0), 0);
+ QCOMPARE(m_model->indexForKeyboardSearch("aa", 0), 1);
+ QCOMPARE(m_model->indexForKeyboardSearch("i", 0), 2);
+ QCOMPARE(m_model->indexForKeyboardSearch("image", 0), 2);
+ QCOMPARE(m_model->indexForKeyboardSearch("image.jpg", 0), 2);
+ QCOMPARE(m_model->indexForKeyboardSearch("image.png", 0), 3);
+ QCOMPARE(m_model->indexForKeyboardSearch("t", 0), 4);
+ QCOMPARE(m_model->indexForKeyboardSearch("text", 0), 4);
+ QCOMPARE(m_model->indexForKeyboardSearch("text1", 0), 5);
+ QCOMPARE(m_model->indexForKeyboardSearch("text2", 0), 6);
+ QCOMPARE(m_model->indexForKeyboardSearch("text11", 0), 7);
+
+ // Start a search somewhere in the middle
+ QCOMPARE(m_model->indexForKeyboardSearch("a", 1), 1);
+ QCOMPARE(m_model->indexForKeyboardSearch("i", 3), 3);
+ QCOMPARE(m_model->indexForKeyboardSearch("t", 5), 5);
+ QCOMPARE(m_model->indexForKeyboardSearch("text1", 6), 7);
+
+ // Test searches that go past the last item back to index 0
+ QCOMPARE(m_model->indexForKeyboardSearch("a", 2), 0);
+ QCOMPARE(m_model->indexForKeyboardSearch("i", 7), 2);
+ QCOMPARE(m_model->indexForKeyboardSearch("image.jpg", 3), 2);
+ QCOMPARE(m_model->indexForKeyboardSearch("text2", 7), 6);
+
+ // Test searches that yield no result
+ QCOMPARE(m_model->indexForKeyboardSearch("aaa", 0), -1);
+ QCOMPARE(m_model->indexForKeyboardSearch("b", 0), -1);
+ QCOMPARE(m_model->indexForKeyboardSearch("image.svg", 0), -1);
+ QCOMPARE(m_model->indexForKeyboardSearch("text3", 0), -1);
+ QCOMPARE(m_model->indexForKeyboardSearch("text3", 5), -1);
+
+ // Test upper case searches (note that search is case insensitive)
+ QCOMPARE(m_model->indexForKeyboardSearch("A", 0), 0);
+ QCOMPARE(m_model->indexForKeyboardSearch("aA", 0), 1);
+ QCOMPARE(m_model->indexForKeyboardSearch("TexT", 5), 5);
+ QCOMPARE(m_model->indexForKeyboardSearch("IMAGE", 4), 2);
+
+ // TODO: Maybe we should also test keyboard searches in directories which are not sorted by Name?
+}
+
bool KFileItemModelTest::isModelConsistent() const
{
for (int i = 0; i < m_model->count(); ++i) {
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2011 by Frank Reininghaus <frank78ac@googlemail.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include <qtest_kde.h>
+
+#include "kitemviews/kitemlistkeyboardsearchmanager_p.h"
+
+class KItemListKeyboardSearchManagerTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void init();
+
+ void testBasicKeyboardSearch();
+ void testAbortedKeyboardSearch();
+ void testRepeatedKeyPress();
+
+private:
+ KItemListKeyboardSearchManager m_keyboardSearchManager;
+};
+
+void KItemListKeyboardSearchManagerTest::init()
+{
+ // Make sure that the previous search string is cleared
+ m_keyboardSearchManager.addKeys("");
+}
+
+void KItemListKeyboardSearchManagerTest::testBasicKeyboardSearch()
+{
+ QSignalSpy spy(&m_keyboardSearchManager, SIGNAL(changeCurrentItem(QString,bool)));
+
+ m_keyboardSearchManager.addKeys("f");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "f" << true);
+
+ m_keyboardSearchManager.addKeys("i");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "fi" << false);
+
+ m_keyboardSearchManager.addKeys("l");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "fil" << false);
+
+ m_keyboardSearchManager.addKeys("e");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "file" << false);
+}
+
+void KItemListKeyboardSearchManagerTest::testAbortedKeyboardSearch()
+{
+ QSignalSpy spy(&m_keyboardSearchManager, SIGNAL(changeCurrentItem(QString,bool)));
+
+ m_keyboardSearchManager.addKeys("f");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "f" << true);
+
+ m_keyboardSearchManager.addKeys("i");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "fi" << false);
+
+ // If the delay between two key presses is larger than QApplication::keyboardInputInterval(),
+ // a new search is started. We add a small safety margin to avoid race conditions.
+ QTest::qWait(QApplication::keyboardInputInterval() + 10);
+
+ m_keyboardSearchManager.addKeys("l");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "l" << true);
+
+ m_keyboardSearchManager.addKeys("e");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "le" << false);
+}
+
+void KItemListKeyboardSearchManagerTest::testRepeatedKeyPress()
+{
+ // If the same key is pressed repeatedly, the next matching item should be highlighted after
+ // each key press. To achieve, that, the manager emits the changeCurrentItem(QString,bool)
+ // signal, where
+ // 1. the string contains the repeated key only once, and
+ // 2. the bool searchFromNextItem is true.
+
+ QSignalSpy spy(&m_keyboardSearchManager, SIGNAL(changeCurrentItem(QString,bool)));
+
+ m_keyboardSearchManager.addKeys("p");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "p" << true);
+
+ m_keyboardSearchManager.addKeys("p");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "p" << true);
+
+ m_keyboardSearchManager.addKeys("p");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "p" << true);
+
+ // Now press another key -> the search string contains all pressed keys
+ m_keyboardSearchManager.addKeys("q");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "pppq" << false);
+}
+
+QTEST_KDEMAIN(KItemListKeyboardSearchManagerTest, NoGUI)
+
+#include "kitemlistkeyboardsearchmanagertest.moc"