Improve search.

Find as you type.
Add shortcut.
Hide search widgets when not used.

Refs #24
This commit is contained in:
Florian Geyer 2012-05-19 11:53:32 +02:00
parent 4a1423da88
commit e4a5cd214f
10 changed files with 182 additions and 39 deletions

View File

@ -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

View File

@ -364,6 +364,11 @@ void DatabaseTabWidget::deleteGroup()
currentDatabaseWidget()->deleteGroup();
}
void DatabaseTabWidget::toggleSearch()
{
currentDatabaseWidget()->toggleSearch();
}
void DatabaseTabWidget::updateTabName(Database* db)
{
int index = databaseIndex(db);

View File

@ -71,6 +71,7 @@ public Q_SLOTS:
void createGroup();
void editGroup();
void deleteGroup();
void toggleSearch();
Q_SIGNALS:
void entrySelectionChanged(bool singleEntrySelected);

View File

@ -16,13 +16,17 @@
*/
#include "DatabaseWidget.h"
#include "ui_SearchWidget.h"
#include <QtCore/QTimer>
#include <QtGui/QAction>
#include <QtGui/QHBoxLayout>
#include <QtGui/QLabel>
#include <QtGui/QSplitter>
#include <QtGui/QLineEdit>
#include <QtGui/QMessageBox>
#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<Entry*> searchResult = searchGroup->search(m_searchEdit->text(), Qt::CaseInsensitive);
QList<Entry*> 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();
}

View File

@ -18,11 +18,8 @@
#ifndef KEEPASSX_DATABASEWIDGET_H
#define KEEPASSX_DATABASEWIDGET_H
#include <QtGui/QPushButton>
#include <QtGui/QStackedWidget>
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<Ui::SearchWidget> 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

View File

@ -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);

View File

@ -100,6 +100,7 @@
<addaction name="actionEntryNew"/>
<addaction name="actionEntryEdit"/>
<addaction name="actionEntryDelete"/>
<addaction name="actionSearch"/>
</widget>
<action name="actionQuit">
<property name="text">
@ -225,6 +226,17 @@
<string>Clone entry</string>
</property>
</action>
<action name="actionSearch">
<property name="checkable">
<bool>true</bool>
</property>
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Find</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

48
src/gui/SearchWidget.ui Normal file
View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SearchWidget</class>
<widget class="QWidget" name="SearchWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>466</width>
<height>53</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolButton" name="closeSearchButton">
<property name="text">
<string/>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Find:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="searchEdit"/>
</item>
<item>
<widget class="QPushButton" name="clearSearchButton">
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -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;

View File

@ -155,25 +155,30 @@ void TestGui::testSearch()
DatabaseTabWidget* tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
DatabaseWidget* dbWidget = tabWidget->currentDatabaseWidget();
QAction* serachAction = m_mainWindow->findChild<QAction*>("actionSearch");
QVERIFY(serachAction->isEnabled());
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("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*>("entryView");
QLineEdit* searchEdit = dbWidget->findChild<QLineEdit*>("searchEdit");
QPushButton* clearSearch = dbWidget->findChild<QPushButton*>("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<QToolBar*>("toolBar");
QModelIndex item = entryView->model()->index(0, 1);
QRect itemRect = entryView->visualRect(item);
QTest::mouseClick(entryView->viewport(), Qt::LeftButton, Qt::NoModifier, itemRect.center());