mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-04 08:55:31 -05:00
Improve search.
Find as you type. Add shortcut. Hide search widgets when not used. Refs #24
This commit is contained in:
parent
4a1423da88
commit
e4a5cd214f
@ -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
|
||||
|
@ -364,6 +364,11 @@ void DatabaseTabWidget::deleteGroup()
|
||||
currentDatabaseWidget()->deleteGroup();
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::toggleSearch()
|
||||
{
|
||||
currentDatabaseWidget()->toggleSearch();
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::updateTabName(Database* db)
|
||||
{
|
||||
int index = databaseIndex(db);
|
||||
|
@ -71,6 +71,7 @@ public Q_SLOTS:
|
||||
void createGroup();
|
||||
void editGroup();
|
||||
void deleteGroup();
|
||||
void toggleSearch();
|
||||
|
||||
Q_SIGNALS:
|
||||
void entrySelectionChanged(bool singleEntrySelected);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
48
src/gui/SearchWidget.ui
Normal 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>
|
@ -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;
|
||||
|
@ -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());
|
||||
|
Loading…
x
Reference in New Issue
Block a user