Autocomplete usernames based on most frequent in database

* Fixes #3126
* Limit autocompletion to the top ten used usernames
- Load common usernames when database is opened
- Transition from QLineEdit to QComboBox for usernames
- Dropdown menu of the combobox lets user choose a common username
- Common usernames are autocompleted via inline completion
- Common usernames are sorted by frequency (first) and name (second)
This commit is contained in:
Matthias Drexler 2019-06-22 15:38:02 +02:00 committed by Jonathan White
parent a22e8a1f40
commit f85642741d
13 changed files with 134 additions and 10 deletions

View file

@ -1181,3 +1181,29 @@ void TestGroup::testApplyGroupIconRecursively()
QVERIFY(subsubgroup->iconNumber() == iconForGroups);
QVERIFY(subsubgroupEntry->iconNumber() == iconForEntries);
}
void TestGroup::testUsernamesRecursive()
{
Database* database = new Database();
// Create a subgroup
Group* subgroup = new Group();
subgroup->setName("Subgroup");
subgroup->setParent(database->rootGroup());
// Generate entries in the root group and the subgroup
Entry* rootGroupEntry = database->rootGroup()->addEntryWithPath("Root group entry");
rootGroupEntry->setUsername("Name1");
Entry* subgroupEntry = subgroup->addEntryWithPath("Subgroup entry");
subgroupEntry->setUsername("Name2");
Entry* subgroupEntryReusingUsername = subgroup->addEntryWithPath("Another subgroup entry");
subgroupEntryReusingUsername->setUsername("Name2");
QList<QString> usernames = database->rootGroup()->usernamesRecursive();
QCOMPARE(usernames.size(), 2);
QVERIFY(usernames.contains("Name1"));
QVERIFY(usernames.contains("Name2"));
QVERIFY(usernames.indexOf("Name2") < usernames.indexOf("Name1"));
}

View file

@ -48,6 +48,7 @@ private slots:
void testChildrenSort();
void testHierarchy();
void testApplyGroupIconRecursively();
void testUsernamesRecursive();
};
#endif // KEEPASSX_TESTGROUP_H

View file

@ -507,7 +507,7 @@ void TestGui::testEditEntry()
QVERIFY(okButton);
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
titleEdit->setText("multiline\ntitle");
editEntryWidget->findChild<QLineEdit*>("usernameEdit")->setText("multiline\nusername");
editEntryWidget->findChild<QComboBox*>("usernameComboBox")->lineEdit()->setText("multiline\nusername");
editEntryWidget->findChild<QLineEdit*>("passwordEdit")->setText("multiline\npassword");
editEntryWidget->findChild<QLineEdit*>("passwordRepeatEdit")->setText("multiline\npassword");
editEntryWidget->findChild<QLineEdit*>("urlEdit")->setText("multiline\nurl");
@ -594,6 +594,10 @@ void TestGui::testAddEntry()
auto* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
auto* titleEdit = editEntryWidget->findChild<QLineEdit*>("titleEdit");
QTest::keyClicks(titleEdit, "test");
auto* usernameComboBox = editEntryWidget->findChild<QComboBox*>("usernameComboBox");
QVERIFY(usernameComboBox);
QTest::mouseClick(usernameComboBox, Qt::LeftButton);
QTest::keyClicks(usernameComboBox, "AutocompletionUsername");
auto* editEntryWidgetButtonBox = editEntryWidget->findChild<QDialogButtonBox*>("buttonBox");
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
@ -602,17 +606,31 @@ void TestGui::testAddEntry()
Entry* entry = entryView->entryFromIndex(item);
QCOMPARE(entry->title(), QString("test"));
QCOMPARE(entry->username(), QString("AutocompletionUsername"));
QCOMPARE(entry->historyItems().size(), 0);
m_db->updateCommonUsernames();
// Add entry "something 2"
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
QTest::keyClicks(titleEdit, "something 2");
QTest::mouseClick(usernameComboBox, Qt::LeftButton);
QTest::keyClicks(usernameComboBox, "Auto");
QTest::keyPress(usernameComboBox, Qt::Key_Right);
auto* passwordEdit = editEntryWidget->findChild<QLineEdit*>("passwordEdit");
auto* passwordRepeatEdit = editEntryWidget->findChild<QLineEdit*>("passwordRepeatEdit");
QTest::keyClicks(passwordEdit, "something 2");
QTest::keyClicks(passwordRepeatEdit, "something 2");
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::ViewMode);
item = entryView->model()->index(1, 1);
entry = entryView->entryFromIndex(item);
QCOMPARE(entry->title(), QString("something 2"));
QCOMPARE(entry->username(), QString("AutocompletionUsername"));
QCOMPARE(entry->historyItems().size(), 0);
// Add entry "something 5" but click cancel button (does NOT add entry)
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
QTest::keyClicks(titleEdit, "something 5");
@ -1063,8 +1081,8 @@ void TestGui::testEntryPlaceholders()
auto* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
auto* titleEdit = editEntryWidget->findChild<QLineEdit*>("titleEdit");
QTest::keyClicks(titleEdit, "test");
QLineEdit* usernameEdit = editEntryWidget->findChild<QLineEdit*>("usernameEdit");
QTest::keyClicks(usernameEdit, "john");
QComboBox* usernameComboBox = editEntryWidget->findChild<QComboBox*>("usernameComboBox");
QTest::keyClicks(usernameComboBox, "john");
QLineEdit* urlEdit = editEntryWidget->findChild<QLineEdit*>("urlEdit");
QTest::keyClicks(urlEdit, "{TITLE}.{USERNAME}");
auto* editEntryWidgetButtonBox = editEntryWidget->findChild<QDialogButtonBox*>("buttonBox");