Auto-Type: Shortcuts for selection dialog (#6361)

* Shortcut to toggle search all entries
* Select first match only when we have a window match

When we default to full database search it's possible the user would select the first match without by accident.

In this case when our query is empty we don't select anything and you need to either type something or press down to select the first item.

* Added username, password, and TOTP keyboard shortcuts and a help tip

* Closes #6176

Co-authored-by: Jonathan White <support@dmapps.us>
This commit is contained in:
Toni Spets 2021-04-01 06:27:56 +03:00 committed by GitHub
parent c19efb5b19
commit 371bd2e51b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 112 additions and 44 deletions

View File

@ -509,6 +509,11 @@ void AutoType::resetAutoTypeState()
QList<QSharedPointer<AutoTypeAction>>
AutoType::parseSequence(const QString& entrySequence, const Entry* entry, QString& error, bool syntaxOnly)
{
if (!entry) {
error = tr("Invalid entry provided");
return {};
}
const int maxTypeDelay = 100;
const int maxWaitDelay = 10000;
const int maxRepetition = 100;

View File

@ -78,14 +78,20 @@ void AutoTypeMatchView::keyPressEvent(QKeyEvent* event)
QTableView::keyPressEvent(event);
}
void AutoTypeMatchView::setMatchList(const QList<AutoTypeMatch>& matches)
void AutoTypeMatchView::setMatchList(const QList<AutoTypeMatch>& matches, bool selectFirst)
{
m_model->setMatchList(matches);
m_sortModel->setFilterWildcard({});
horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
if (selectFirst) {
selectionModel()->setCurrentIndex(m_sortModel->index(0, 0),
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
} else {
selectionModel()->clear();
}
emit currentMatchChanged(currentMatch());
}

View File

@ -34,7 +34,7 @@ public:
explicit AutoTypeMatchView(QWidget* parent = nullptr);
AutoTypeMatch currentMatch();
AutoTypeMatch matchFromIndex(const QModelIndex& index);
void setMatchList(const QList<AutoTypeMatch>& matches);
void setMatchList(const QList<AutoTypeMatch>& matches, bool selectFirst);
void filterList(const QString& filter);
signals:

View File

@ -67,16 +67,14 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
connect(m_ui->search, SIGNAL(returnPressed()), SLOT(activateCurrentMatch()));
connect(&m_searchTimer, SIGNAL(timeout()), SLOT(performSearch()));
connect(m_ui->filterRadio, &QRadioButton::toggled, this, [this](bool checked) {
m_ui->searchCheckBox->setShortcut(Qt::CTRL + Qt::Key_F);
connect(m_ui->searchCheckBox, &QCheckBox::toggled, this, [this](bool checked) {
if (checked) {
// Reset to original match list
m_ui->view->setMatchList(m_matches);
performSearch();
m_ui->search->setFocus();
}
});
connect(m_ui->searchRadio, &QRadioButton::toggled, this, [this](bool checked) {
if (checked) {
} else {
// Reset to original match list
m_ui->view->setMatchList(m_matches, true);
performSearch();
m_ui->search->setFocus();
}
@ -100,24 +98,22 @@ void AutoTypeSelectDialog::setMatches(const QList<AutoTypeMatch>& matches, const
m_matches = matches;
m_dbs = dbs;
m_ui->view->setMatchList(m_matches);
if (m_matches.isEmpty()) {
m_ui->searchRadio->setChecked(true);
} else {
m_ui->filterRadio->setChecked(true);
}
m_ui->view->setMatchList(m_matches, !m_matches.isEmpty() || !m_ui->search->text().isEmpty());
m_ui->searchCheckBox->setChecked(m_matches.isEmpty());
}
void AutoTypeSelectDialog::submitAutoTypeMatch(AutoTypeMatch match)
{
if (match.first) {
m_accepted = true;
accept();
emit matchActivated(std::move(match));
}
}
void AutoTypeSelectDialog::performSearch()
{
if (m_ui->filterRadio->isChecked()) {
if (!m_ui->searchCheckBox->isChecked()) {
m_ui->view->filterList(m_ui->search->text());
return;
}
@ -148,7 +144,7 @@ void AutoTypeSelectDialog::performSearch()
}
}
m_ui->view->setMatchList(matches);
m_ui->view->setMatchList(matches, !m_ui->search->text().isEmpty());
}
void AutoTypeSelectDialog::moveSelectionUp()
@ -164,6 +160,13 @@ void AutoTypeSelectDialog::moveSelectionUp()
void AutoTypeSelectDialog::moveSelectionDown()
{
auto current = m_ui->view->currentIndex();
// special case where we have no default selection (empty search)
if (!current.isValid()) {
m_ui->view->setCurrentIndex(m_ui->view->indexAt({0, 0}));
return;
}
auto next = current.sibling(current.row() + 1, 0);
if (next.isValid()) {
@ -274,16 +277,24 @@ void AutoTypeSelectDialog::buildActionMenu()
m_actionMenu->addAction(copyPasswordAction);
m_actionMenu->addAction(copyTotpAction);
auto shortcut = new QShortcut(Qt::CTRL + Qt::Key_1, this);
connect(shortcut, &QShortcut::activated, typeUsernameAction, &QAction::trigger);
connect(typeUsernameAction, &QAction::triggered, this, [&] {
auto match = m_ui->view->currentMatch();
match.second = "{USERNAME}";
submitAutoTypeMatch(match);
});
shortcut = new QShortcut(Qt::CTRL + Qt::Key_2, this);
connect(shortcut, &QShortcut::activated, typePasswordAction, &QAction::trigger);
connect(typePasswordAction, &QAction::triggered, this, [&] {
auto match = m_ui->view->currentMatch();
match.second = "{PASSWORD}";
submitAutoTypeMatch(match);
});
shortcut = new QShortcut(Qt::CTRL + Qt::Key_3, this);
connect(shortcut, &QShortcut::activated, typeTotpAction, &QAction::trigger);
connect(typeTotpAction, &QAction::triggered, this, [&] {
auto match = m_ui->view->currentMatch();
match.second = "{TOTP}";

View File

@ -7,13 +7,21 @@
<x>0</x>
<y>0</y>
<width>418</width>
<height>295</height>
<height>303</height>
</rect>
</property>
<property name="windowTitle">
<string>Auto-Type - KeePassXC</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="topMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
@ -21,6 +29,53 @@
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>14</width>
<height>14</height>
</size>
</property>
<property name="toolTip">
<string>&lt;p&gt;You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:&lt;br/&gt;
Ctrl+F - Toggle database search&lt;br/&gt;
Ctrl+1 - Type username&lt;br/&gt;
Ctrl+2 - Type password&lt;br/&gt;
Ctrl+3 - Type TOTP&lt;/p&gt;</string>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../share/icons/icons.qrc">:/icons/application/scalable/actions/system-help.svg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="AutoTypeMatchView" name="view">
<property name="sizePolicy">
@ -86,19 +141,9 @@
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="filterRadio">
<widget class="QCheckBox" name="searchCheckBox">
<property name="text">
<string>&amp;Filter Matches</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="searchRadio">
<property name="text">
<string>&amp;Search Database</string>
<string>Search all open databases</string>
</property>
</widget>
</item>
@ -133,7 +178,7 @@
</size>
</property>
<property name="placeholderText">
<string>Filter or Search…</string>
<string>Search…</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
@ -188,10 +233,11 @@
</customwidgets>
<tabstops>
<tabstop>view</tabstop>
<tabstop>filterRadio</tabstop>
<tabstop>searchRadio</tabstop>
<tabstop>searchCheckBox</tabstop>
<tabstop>search</tabstop>
</tabstops>
<resources/>
<resources>
<include location="../../share/icons/icons.qrc"/>
</resources>
<connections/>
</ui>