From e4a5cd214fbb1723663599223a25981a13611794 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Sat, 19 May 2012 11:53:32 +0200 Subject: [PATCH] Improve search. Find as you type. Add shortcut. Hide search widgets when not used. Refs #24 --- src/CMakeLists.txt | 1 + src/gui/DatabaseTabWidget.cpp | 5 ++ src/gui/DatabaseTabWidget.h | 1 + src/gui/DatabaseWidget.cpp | 102 +++++++++++++++++++++++++--------- src/gui/DatabaseWidget.h | 19 +++++-- src/gui/MainWindow.cpp | 15 +++++ src/gui/MainWindow.ui | 12 ++++ src/gui/SearchWidget.ui | 48 ++++++++++++++++ src/gui/entry/EntryView.cpp | 1 + tests/gui/TestGui.cpp | 17 ++++-- 10 files changed, 182 insertions(+), 39 deletions(-) create mode 100644 src/gui/SearchWidget.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 838c32f58..2644103cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -127,6 +127,7 @@ set(keepassx_FORMS gui/EditWidget.ui gui/EditWidgetIcons.ui gui/MainWindow.ui + gui/SearchWidget.ui gui/entry/EditEntryWidgetAdvanced.ui gui/entry/EditEntryWidgetHistory.ui gui/entry/EditEntryWidgetMain.ui diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 66a297336..f92284647 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -364,6 +364,11 @@ void DatabaseTabWidget::deleteGroup() currentDatabaseWidget()->deleteGroup(); } +void DatabaseTabWidget::toggleSearch() +{ + currentDatabaseWidget()->toggleSearch(); +} + void DatabaseTabWidget::updateTabName(Database* db) { int index = databaseIndex(db); diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index c8737bef6..a49625f7e 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -71,6 +71,7 @@ public Q_SLOTS: void createGroup(); void editGroup(); void deleteGroup(); + void toggleSearch(); Q_SIGNALS: void entrySelectionChanged(bool singleEntrySelected); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index ac259f8ac..432b68f84 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -16,13 +16,17 @@ */ #include "DatabaseWidget.h" +#include "ui_SearchWidget.h" +#include +#include #include #include #include #include #include +#include "core/DataPath.h" #include "core/Metadata.h" #include "core/Tools.h" #include "gui/ChangeMasterKeyWidget.h" @@ -35,44 +39,47 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) : QStackedWidget(parent) , m_db(db) + , m_searchUi(new Ui::SearchWidget()) + , m_searchWidget(new QWidget()) , m_newGroup(0) , m_newEntry(0) , m_newParent(0) { + + m_searchUi->setupUi(m_searchWidget); + + m_searchTimer = new QTimer(this); + m_searchTimer->setSingleShot(true); + m_mainWidget = new QWidget(this); QLayout* layout = new QHBoxLayout(m_mainWidget); QSplitter* splitter = new QSplitter(m_mainWidget); + QWidget* rightHandSideWidget = new QWidget(splitter); + m_searchWidget->setParent(rightHandSideWidget); + m_groupView = new GroupView(db, splitter); m_groupView->setObjectName("groupView"); - m_entryView = new EntryView(splitter); + + m_entryView = new EntryView(rightHandSideWidget); m_entryView->setObjectName("entryView"); QSizePolicy policy; - policy = m_groupView->sizePolicy(); policy.setHorizontalStretch(30); m_groupView->setSizePolicy(policy); - - QWidget* rightHandSideWidget = new QWidget(); policy = rightHandSideWidget->sizePolicy(); policy.setHorizontalStretch(70); rightHandSideWidget->setSizePolicy(policy); - QVBoxLayout* vLayout = new QVBoxLayout(); - QHBoxLayout* hLayout = new QHBoxLayout(); + QAction* closeAction = new QAction(m_searchWidget); + QIcon closeIcon = dataPath()->icon("action", "dialog-close"); + closeAction->setIcon(closeIcon); + m_searchUi->closeSearchButton->setDefaultAction(closeAction); + m_searchWidget->hide(); - hLayout->addWidget(new QLabel("Find:")); - - m_searchEdit = new QLineEdit(); - m_searchEdit->setObjectName("searchEdit"); - hLayout->addWidget(m_searchEdit); - - m_clearSearchButton = new QPushButton("Clear"); - m_clearSearchButton->setObjectName("clearSearchButton"); - hLayout->addWidget(m_clearSearchButton); - - vLayout->addLayout(hLayout); + QVBoxLayout* vLayout = new QVBoxLayout(rightHandSideWidget); + vLayout->addWidget(m_searchWidget); vLayout->addWidget(m_entryView); rightHandSideWidget->setLayout(vLayout); @@ -112,12 +119,18 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) connect(m_changeMasterKeyWidget, SIGNAL(editFinished(bool)), SLOT(updateMasterKey(bool))); connect(m_databaseSettingsWidget, SIGNAL(editFinished(bool)), SLOT(switchToView(bool))); connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged())); - connect(m_searchEdit, SIGNAL(returnPressed()), this, SLOT(search())); - connect(m_clearSearchButton, SIGNAL(clicked()), this, SLOT(clearSearchEdit())); + connect(m_searchUi->searchEdit, SIGNAL(textChanged(QString)), this, SLOT(startSearchTimer())); + connect(m_searchTimer, SIGNAL(timeout()), this, SLOT(search())); + connect(closeAction, SIGNAL(triggered()), this, SLOT(closeSearch())); + connect(m_searchUi->clearSearchButton, SIGNAL(clicked()), this, SLOT(clearSearchEdit())); setCurrentIndex(0); } +DatabaseWidget::~DatabaseWidget() +{ +} + DatabaseWidget::Mode DatabaseWidget::currentMode() { switch (currentIndex()) { @@ -345,20 +358,60 @@ void DatabaseWidget::switchToDatabaseSettings() setCurrentIndex(4); } +void DatabaseWidget::toggleSearch() +{ + if (m_entryView->inSearch()) { + closeSearch(); + } + else { + showSearch(); + } +} + +void DatabaseWidget::closeSearch() +{ + Q_ASSERT(m_lastGroup); + m_groupView->setCurrentGroup(m_lastGroup); +} + +void DatabaseWidget::showSearch() +{ + m_searchWidget->show(); + m_searchUi->searchEdit->blockSignals(true); + m_searchUi->searchEdit->clear(); + m_searchUi->searchEdit->blockSignals(false); + search(); + m_searchUi->searchEdit->setFocus(); +} + +void DatabaseWidget::clearSearchEdit() +{ + m_searchUi->searchEdit->clear(); + m_searchUi->searchEdit->setFocus(); +} + void DatabaseWidget::search() { Group* searchGroup = m_db->rootGroup(); - QList searchResult = searchGroup->search(m_searchEdit->text(), Qt::CaseInsensitive); + QList searchResult = searchGroup->search(m_searchUi->searchEdit->text(), Qt::CaseInsensitive); Group* group = m_groupView->currentGroup(); if (group) { m_lastGroup = m_groupView->currentGroup(); + m_groupView->setCurrentIndex(QModelIndex()); } - m_groupView->setCurrentIndex(QModelIndex()); m_entryView->search(searchResult); } +void DatabaseWidget::startSearchTimer() +{ + if (!m_searchTimer->isActive()) { + m_searchTimer->stop(); + } + m_searchTimer->start(100); +} + bool DatabaseWidget::dbHasKey() { return m_db->hasKey(); @@ -383,11 +436,6 @@ void DatabaseWidget::clearLastGroup(Group* group) { if (group) { m_lastGroup = 0; + m_searchWidget->hide(); } } - -void DatabaseWidget::clearSearchEdit() -{ - m_searchEdit->clear(); - m_searchEdit->setFocus(); -} diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 2a0b4bd02..60abce38b 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -18,11 +18,8 @@ #ifndef KEEPASSX_DATABASEWIDGET_H #define KEEPASSX_DATABASEWIDGET_H -#include #include -class QLineEdit; - class ChangeMasterKeyWidget; class DatabaseSettingsWidget; class Database; @@ -33,6 +30,10 @@ class EntryView; class Group; class GroupView; +namespace Ui { + class SearchWidget; +} + class DatabaseWidget : public QStackedWidget { Q_OBJECT @@ -46,6 +47,7 @@ public: }; explicit DatabaseWidget(Database* db, QWidget* parent = 0); + ~DatabaseWidget(); GroupView* groupView(); EntryView* entryView(); bool dbHasKey(); @@ -69,7 +71,7 @@ public Q_SLOTS: void switchToGroupEdit(); void switchToMasterKeyChange(); void switchToDatabaseSettings(); - void search(); + void toggleSearch(); private Q_SLOTS: void switchBackToEntryEdit(); @@ -82,11 +84,17 @@ private Q_SLOTS: void emitCurrentModeChanged(); void clearLastGroup(Group* group); void clearSearchEdit(); + void search(); + void startSearchTimer(); + void showSearch(); + void closeSearch(); private: void truncateHistories(); Database* const m_db; + const QScopedPointer m_searchUi; + QWidget* const m_searchWidget; QWidget* m_mainWidget; EditEntryWidget* m_editEntryWidget; EditEntryWidget* m_historyEditEntryWidget; @@ -98,9 +106,8 @@ private: Group* m_newGroup; Entry* m_newEntry; Group* m_newParent; - QLineEdit* m_searchEdit; - QPushButton* m_clearSearchButton; Group* m_lastGroup; + QTimer* m_searchTimer; }; #endif // KEEPASSX_DATABASEWIDGET_H diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index dfc98f792..add612b82 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -40,6 +40,7 @@ MainWindow::MainWindow() setShortcut(m_ui->actionDatabaseSaveAs, QKeySequence::SaveAs); setShortcut(m_ui->actionDatabaseClose, QKeySequence::Close, Qt::CTRL + Qt::Key_W); setShortcut(m_ui->actionQuit, QKeySequence::Quit, Qt::CTRL + Qt::Key_Q); + setShortcut(m_ui->actionSearch, QKeySequence::Find, Qt::CTRL + Qt::Key_F); connect(m_ui->tabWidget, SIGNAL(entrySelectionChanged(bool)), SLOT(setMenuActionState())); @@ -84,6 +85,9 @@ MainWindow::MainWindow() SLOT(editGroup())); connect(m_ui->actionGroupDelete, SIGNAL(triggered()), m_ui->tabWidget, SLOT(deleteGroup())); + + connect(m_ui->actionSearch, SIGNAL(triggered()), m_ui->tabWidget, + SLOT(toggleSearch())); } MainWindow::~MainWindow() @@ -148,6 +152,13 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionGroupDelete->setEnabled(false); } } + m_ui->actionSearch->setEnabled(true); + if (dbWidget->entryView()->inSearch()) { + m_ui->actionSearch->setChecked(true); + } + else { + m_ui->actionSearch->setChecked(false); + } m_ui->actionChangeMasterKey->setEnabled(true); m_ui->actionChangeDatabaseSettings->setEnabled(true); m_ui->actionDatabaseSave->setEnabled(true); @@ -161,6 +172,8 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionGroupEdit->setEnabled(false); m_ui->actionEntryDelete->setEnabled(false); m_ui->actionGroupDelete->setEnabled(false); + m_ui->actionSearch->setEnabled(false); + m_ui->actionSearch->setChecked(false); m_ui->actionChangeMasterKey->setEnabled(false); m_ui->actionChangeDatabaseSettings->setEnabled(false); m_ui->actionDatabaseSave->setEnabled(false); @@ -179,6 +192,8 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionGroupEdit->setEnabled(false); m_ui->actionEntryDelete->setEnabled(false); m_ui->actionGroupDelete->setEnabled(false); + m_ui->actionSearch->setEnabled(false); + m_ui->actionSearch->setChecked(false); m_ui->actionChangeMasterKey->setEnabled(false); m_ui->actionChangeDatabaseSettings->setEnabled(false); m_ui->actionDatabaseSave->setEnabled(false); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index ff8c99f0a..2ce9c2670 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -100,6 +100,7 @@ + @@ -225,6 +226,17 @@ Clone entry + + + true + + + false + + + Find + + diff --git a/src/gui/SearchWidget.ui b/src/gui/SearchWidget.ui new file mode 100644 index 000000000..7efc4bee6 --- /dev/null +++ b/src/gui/SearchWidget.ui @@ -0,0 +1,48 @@ + + + SearchWidget + + + + 0 + 0 + 466 + 53 + + + + + + + + + + + + + true + + + + + + + Find: + + + + + + + + + + Clear + + + + + + + + diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index a89b145fa..e376d8434 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -104,6 +104,7 @@ Entry* EntryView::entryFromIndex(const QModelIndex& index) void EntryView::switchToSearch() { + sortByColumn(1, Qt::AscendingOrder); // TODO: should probably be improved sortByColumn(0, Qt::AscendingOrder); showColumn(0); m_inSearch = true; diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 9647e70e7..7dc6a1f97 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -155,25 +155,30 @@ void TestGui::testSearch() DatabaseTabWidget* tabWidget = m_mainWindow->findChild("tabWidget"); DatabaseWidget* dbWidget = tabWidget->currentDatabaseWidget(); + QAction* serachAction = m_mainWindow->findChild("actionSearch"); + QVERIFY(serachAction->isEnabled()); + QToolBar* toolBar = m_mainWindow->findChild("toolBar"); + QWidget* serachActionWidget = toolBar->widgetForAction(serachAction); + QVERIFY(serachActionWidget->isVisible()); + QVERIFY(serachActionWidget->isEnabled()); + QTest::mouseClick(serachActionWidget, Qt::LeftButton); + QTest::qWait(20); + EntryView* entryView = dbWidget->findChild("entryView"); QLineEdit* searchEdit = dbWidget->findChild("searchEdit"); QPushButton* clearSearch = dbWidget->findChild("clearSearchButton"); QTest::keyClicks(searchEdit, "ZZZ"); - QTest::keyClick(searchEdit, Qt::Key_Return); - QTest::qWait(20); + QTest::qWait(120); QCOMPARE(entryView->model()->rowCount(), 0); QTest::mouseClick(clearSearch, Qt::LeftButton); QTest::keyClicks(searchEdit, "some"); - QTest::keyClick(searchEdit, Qt::Key_Return); - QTest::qWait(20); + QTest::qWait(120); QCOMPARE(entryView->model()->rowCount(), 2); - QToolBar* toolBar = m_mainWindow->findChild("toolBar"); - QModelIndex item = entryView->model()->index(0, 1); QRect itemRect = entryView->visualRect(item); QTest::mouseClick(entryView->viewport(), Qt::LeftButton, Qt::NoModifier, itemRect.center());