mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-08-08 06:22:53 -04:00
Add "press enter to search" option (#12263)
* Also increase auto-search timeout to 500 ms to improve user experience, especially with large databases. The previous value of 100ms guaranteed a search was performed after every character entered, even when typing relatively fast. --------- Co-authored-by: Jonathan White <support@dmapps.us>
This commit is contained in:
parent
76b2f377df
commit
7ec0f1f5a8
5 changed files with 79 additions and 14 deletions
|
@ -9955,6 +9955,10 @@ This option is deprecated, use --set-key-file instead.</source>
|
||||||
<source>Limit search to selected group</source>
|
<source>Limit search to selected group</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press Enter to search</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>SettingsClientModel</name>
|
<name>SettingsClientModel</name>
|
||||||
|
|
|
@ -98,6 +98,7 @@ public:
|
||||||
GUI_CompactMode,
|
GUI_CompactMode,
|
||||||
GUI_CheckForUpdates,
|
GUI_CheckForUpdates,
|
||||||
GUI_CheckForUpdatesIncludeBetas,
|
GUI_CheckForUpdatesIncludeBetas,
|
||||||
|
SearchWaitForEnter,
|
||||||
GUI_ShowExpiredEntriesOnDatabaseUnlock,
|
GUI_ShowExpiredEntriesOnDatabaseUnlock,
|
||||||
GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays,
|
GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays,
|
||||||
GUI_FontSizeOffset,
|
GUI_FontSizeOffset,
|
||||||
|
|
|
@ -54,6 +54,7 @@ SearchWidget::SearchWidget(QWidget* parent)
|
||||||
connect(m_searchTimer, SIGNAL(timeout()), SLOT(startSearch()));
|
connect(m_searchTimer, SIGNAL(timeout()), SLOT(startSearch()));
|
||||||
connect(m_clearSearchTimer, SIGNAL(timeout()), SLOT(clearSearch()));
|
connect(m_clearSearchTimer, SIGNAL(timeout()), SLOT(clearSearch()));
|
||||||
connect(this, SIGNAL(escapePressed()), SLOT(clearSearch()));
|
connect(this, SIGNAL(escapePressed()), SLOT(clearSearch()));
|
||||||
|
connect(m_ui->searchEdit, &QLineEdit::returnPressed, this, &SearchWidget::onReturnPressed);
|
||||||
|
|
||||||
m_ui->searchEdit->setPlaceholderText(tr("Search (%1)…", "Search placeholder text, %1 is the keyboard shortcut")
|
m_ui->searchEdit->setPlaceholderText(tr("Search (%1)…", "Search placeholder text, %1 is the keyboard shortcut")
|
||||||
.arg(QKeySequence(QKeySequence::Find).toString(QKeySequence::NativeText)));
|
.arg(QKeySequence(QKeySequence::Find).toString(QKeySequence::NativeText)));
|
||||||
|
@ -69,6 +70,12 @@ SearchWidget::SearchWidget(QWidget* parent)
|
||||||
m_actionLimitGroup->setCheckable(true);
|
m_actionLimitGroup->setCheckable(true);
|
||||||
m_actionLimitGroup->setChecked(config()->get(Config::SearchLimitGroup).toBool());
|
m_actionLimitGroup->setChecked(config()->get(Config::SearchLimitGroup).toBool());
|
||||||
|
|
||||||
|
m_actionWaitForEnter = m_searchMenu->addAction(
|
||||||
|
tr("Press Enter to search"), this, [](bool state) { config()->set(Config::SearchWaitForEnter, state); });
|
||||||
|
m_actionWaitForEnter->setObjectName("actionSearchWaitForEnter");
|
||||||
|
m_actionWaitForEnter->setCheckable(true);
|
||||||
|
m_actionWaitForEnter->setChecked(config()->get(Config::SearchWaitForEnter).toBool());
|
||||||
|
|
||||||
m_ui->searchIcon->setIcon(icons()->icon("system-search"));
|
m_ui->searchIcon->setIcon(icons()->icon("system-search"));
|
||||||
m_ui->searchEdit->addAction(m_ui->searchIcon, QLineEdit::LeadingPosition);
|
m_ui->searchEdit->addAction(m_ui->searchIcon, QLineEdit::LeadingPosition);
|
||||||
|
|
||||||
|
@ -153,12 +160,12 @@ void SearchWidget::connectSignals(SignalMultiplexer& mx)
|
||||||
mx.connect(SIGNAL(entrySelectionChanged()), this, SLOT(resetSearchClearTimer()));
|
mx.connect(SIGNAL(entrySelectionChanged()), this, SLOT(resetSearchClearTimer()));
|
||||||
mx.connect(SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(resetSearchClearTimer()));
|
mx.connect(SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(resetSearchClearTimer()));
|
||||||
mx.connect(SIGNAL(databaseUnlocked()), this, SLOT(focusSearch()));
|
mx.connect(SIGNAL(databaseUnlocked()), this, SLOT(focusSearch()));
|
||||||
mx.connect(m_ui->searchEdit, SIGNAL(returnPressed()), SLOT(switchToEntryEdit()));
|
mx.connect(this, SIGNAL(enterPressed()), SLOT(switchToEntryEdit()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchWidget::databaseChanged(DatabaseWidget* dbWidget)
|
void SearchWidget::databaseChanged(DatabaseWidget* dbWidget)
|
||||||
{
|
{
|
||||||
if (dbWidget != nullptr) {
|
if (dbWidget) {
|
||||||
// Set current search text from this database
|
// Set current search text from this database
|
||||||
m_ui->searchEdit->setText(dbWidget->getCurrentSearch());
|
m_ui->searchEdit->setText(dbWidget->getCurrentSearch());
|
||||||
// Enforce search policy
|
// Enforce search policy
|
||||||
|
@ -171,18 +178,15 @@ void SearchWidget::databaseChanged(DatabaseWidget* dbWidget)
|
||||||
|
|
||||||
void SearchWidget::startSearchTimer()
|
void SearchWidget::startSearchTimer()
|
||||||
{
|
{
|
||||||
if (!m_searchTimer->isActive()) {
|
if (m_actionWaitForEnter->isChecked()) {
|
||||||
m_searchTimer->stop();
|
m_searchTimer->stop();
|
||||||
|
} else {
|
||||||
|
m_searchTimer->start(500);
|
||||||
}
|
}
|
||||||
m_searchTimer->start(100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchWidget::startSearch()
|
void SearchWidget::startSearch()
|
||||||
{
|
{
|
||||||
if (!m_searchTimer->isActive()) {
|
|
||||||
m_searchTimer->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ui->saveIcon->setVisible(true);
|
m_ui->saveIcon->setVisible(true);
|
||||||
search(m_ui->searchEdit->text());
|
search(m_ui->searchEdit->text());
|
||||||
}
|
}
|
||||||
|
@ -200,18 +204,18 @@ void SearchWidget::updateCaseSensitive()
|
||||||
emit caseSensitiveChanged(m_actionCaseSensitive->isChecked());
|
emit caseSensitiveChanged(m_actionCaseSensitive->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchWidget::setCaseSensitive(bool state)
|
|
||||||
{
|
|
||||||
m_actionCaseSensitive->setChecked(state);
|
|
||||||
updateCaseSensitive();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SearchWidget::updateLimitGroup()
|
void SearchWidget::updateLimitGroup()
|
||||||
{
|
{
|
||||||
config()->set(Config::SearchLimitGroup, m_actionLimitGroup->isChecked());
|
config()->set(Config::SearchLimitGroup, m_actionLimitGroup->isChecked());
|
||||||
emit limitGroupChanged(m_actionLimitGroup->isChecked());
|
emit limitGroupChanged(m_actionLimitGroup->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SearchWidget::setCaseSensitive(bool state)
|
||||||
|
{
|
||||||
|
m_actionCaseSensitive->setChecked(state);
|
||||||
|
updateCaseSensitive();
|
||||||
|
}
|
||||||
|
|
||||||
void SearchWidget::setLimitGroup(bool state)
|
void SearchWidget::setLimitGroup(bool state)
|
||||||
{
|
{
|
||||||
m_actionLimitGroup->setChecked(state);
|
m_actionLimitGroup->setChecked(state);
|
||||||
|
@ -244,3 +248,12 @@ void SearchWidget::showSearchMenu()
|
||||||
{
|
{
|
||||||
m_searchMenu->exec(m_ui->searchEdit->mapToGlobal(m_ui->searchEdit->rect().bottomLeft()));
|
m_searchMenu->exec(m_ui->searchEdit->mapToGlobal(m_ui->searchEdit->rect().bottomLeft()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SearchWidget::onReturnPressed()
|
||||||
|
{
|
||||||
|
if (m_actionWaitForEnter->isChecked()) {
|
||||||
|
emit search(m_ui->searchEdit->text());
|
||||||
|
} else {
|
||||||
|
emit enterPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ public slots:
|
||||||
void clearSearch();
|
void clearSearch();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void onReturnPressed();
|
||||||
void startSearchTimer();
|
void startSearchTimer();
|
||||||
void startSearch();
|
void startSearch();
|
||||||
void updateCaseSensitive();
|
void updateCaseSensitive();
|
||||||
|
@ -83,6 +84,7 @@ private:
|
||||||
QTimer* m_clearSearchTimer;
|
QTimer* m_clearSearchTimer;
|
||||||
QAction* m_actionCaseSensitive;
|
QAction* m_actionCaseSensitive;
|
||||||
QAction* m_actionLimitGroup;
|
QAction* m_actionLimitGroup;
|
||||||
|
QAction* m_actionWaitForEnter;
|
||||||
QMenu* m_searchMenu;
|
QMenu* m_searchMenu;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1126,6 +1126,7 @@ void TestGui::testSearch()
|
||||||
auto* searchWidget = toolBar->findChild<SearchWidget*>("SearchWidget");
|
auto* searchWidget = toolBar->findChild<SearchWidget*>("SearchWidget");
|
||||||
QVERIFY(searchWidget->isEnabled());
|
QVERIFY(searchWidget->isEnabled());
|
||||||
auto* searchTextEdit = searchWidget->findChild<QLineEdit*>("searchEdit");
|
auto* searchTextEdit = searchWidget->findChild<QLineEdit*>("searchEdit");
|
||||||
|
auto* waitForEnterAction = searchWidget->findChild<QAction*>("actionSearchWaitForEnter");
|
||||||
|
|
||||||
auto* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
auto* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||||
QVERIFY(entryView->isVisible());
|
QVERIFY(entryView->isVisible());
|
||||||
|
@ -1137,6 +1138,50 @@ void TestGui::testSearch()
|
||||||
QVERIFY(helpButton->isVisible());
|
QVERIFY(helpButton->isVisible());
|
||||||
QVERIFY(!helpPanel->isVisible());
|
QVERIFY(!helpPanel->isVisible());
|
||||||
|
|
||||||
|
// Test "wait for enter" toggle
|
||||||
|
QVERIFY(waitForEnterAction->isVisible());
|
||||||
|
QVERIFY(waitForEnterAction->isCheckable());
|
||||||
|
|
||||||
|
// Test search with "wait for enter" disabled (default)
|
||||||
|
searchTextEdit->clear();
|
||||||
|
QTest::keyClicks(searchTextEdit, "ZZZ");
|
||||||
|
QTRY_COMPARE(searchTextEdit->text(), QString("ZZZ"));
|
||||||
|
QTRY_VERIFY(m_dbWidget->isSearchActive());
|
||||||
|
QTRY_COMPARE(entryView->model()->rowCount(), 0);
|
||||||
|
|
||||||
|
// Clear search
|
||||||
|
searchTextEdit->clear();
|
||||||
|
QTRY_VERIFY(!m_dbWidget->isSearchActive());
|
||||||
|
|
||||||
|
// Enable "wait for enter" mode
|
||||||
|
waitForEnterAction->trigger();
|
||||||
|
QVERIFY(waitForEnterAction->isChecked());
|
||||||
|
|
||||||
|
// Test search with "wait for enter" enabled
|
||||||
|
QTest::keyClicks(searchTextEdit, "ZZZ");
|
||||||
|
QTRY_VERIFY(!m_dbWidget->isSearchActive());
|
||||||
|
|
||||||
|
// Press Enter to execute search
|
||||||
|
QTest::keyClick(searchTextEdit, Qt::Key_Return);
|
||||||
|
QTRY_VERIFY(m_dbWidget->isSearchActive());
|
||||||
|
QTRY_COMPARE(entryView->model()->rowCount(), 0);
|
||||||
|
// Check that search remains active even after clearing
|
||||||
|
searchTextEdit->clear();
|
||||||
|
QTRY_VERIFY(m_dbWidget->isSearchActive());
|
||||||
|
|
||||||
|
// Disable "wait for enter" mode
|
||||||
|
waitForEnterAction->trigger();
|
||||||
|
QVERIFY(!waitForEnterAction->isChecked());
|
||||||
|
|
||||||
|
// Test search with "wait for enter" disabled again
|
||||||
|
QTest::keyClicks(searchTextEdit, "ZZZ");
|
||||||
|
QTRY_VERIFY(m_dbWidget->isSearchActive());
|
||||||
|
QTRY_COMPARE(entryView->model()->rowCount(), 0);
|
||||||
|
|
||||||
|
// Clear search
|
||||||
|
searchTextEdit->clear();
|
||||||
|
QTRY_VERIFY(!m_dbWidget->isSearchActive());
|
||||||
|
|
||||||
// Enter search
|
// Enter search
|
||||||
QTest::mouseClick(searchTextEdit, Qt::LeftButton);
|
QTest::mouseClick(searchTextEdit, Qt::LeftButton);
|
||||||
QTRY_VERIFY(searchTextEdit->hasFocus());
|
QTRY_VERIFY(searchTextEdit->hasFocus());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue