]> cloud.milkyroute.net Git - dolphin.git/commitdiff
KItemListKeyboardSearchManager improvements and unit tests
authorFrank Reininghaus <frank78ac@googlemail.com>
Tue, 30 Aug 2011 11:10:38 +0000 (13:10 +0200)
committerFrank Reininghaus <frank78ac@googlemail.com>
Tue, 30 Aug 2011 16:22:56 +0000 (18:22 +0200)
This commit implements a 'repeated key search' feature, similar
to QAbstractItemView, and adds unit tests for keyboard searching.

src/kitemviews/kitemlistkeyboardsearchmanager.cpp
src/kitemviews/kitemlistkeyboardsearchmanager_p.h
src/tests/CMakeLists.txt
src/tests/kfileitemmodeltest.cpp
src/tests/kitemlistkeyboardsearchmanagertest.cpp [new file with mode: 0644]

index 9552a75820ac9292446aa8bcfe885f526170b399..cefedfc2c87042131240cd4488743c8fa5c88313 100644 (file)
@@ -45,10 +45,23 @@ void KItemListKeyboardSearchManager::addKeys(const QString& keys)
         || !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();
 }
index 3441dd7bce9d20de47c036c25aab243388668380..05de76a8caf9ac0660d18652f0b08811c9e0127a 100644 (file)
 #include <QString>
 #include <QElapsedTimer>
 
-class KItemListController;
-class QInputMethodEvent;
-class QKeyEvent;
-
 /**
  * @brief Controls the keyboard searching ability for a KItemListController.
  *
index b35dc3b8b81c3bbb17136e8e398153b3e56379a0..d8f2250e9035eb46d1dab319a9460662aa9b1601 100644 (file)
@@ -32,6 +32,14 @@ set(kfileitemmodeltest_SRCS
 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
index 091632eab3840cbb01949367e3d153a704534eed..f8398c57816b07726f9f93bcd261c510c6d7d0a4 100644 (file)
@@ -1,5 +1,6 @@
 /***************************************************************************
  *   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  *
@@ -49,6 +50,8 @@ private slots:
     void testExpansionLevelsCompare_data();
     void testExpansionLevelsCompare();
 
+    void testIndexForKeyboardSearch();
+
 private:
     bool isModelConsistent() const;
 
@@ -327,6 +330,56 @@ void KFileItemModelTest::testExpansionLevelsCompare()
     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) {
diff --git a/src/tests/kitemlistkeyboardsearchmanagertest.cpp b/src/tests/kitemlistkeyboardsearchmanagertest.cpp
new file mode 100644 (file)
index 0000000..be48393
--- /dev/null
@@ -0,0 +1,121 @@
+/***************************************************************************
+ *   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"