From fe1189ea7973466e929810a42b247c284cd26f42 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sun, 8 Mar 2020 22:22:01 -0400 Subject: [PATCH] Enhance Password Editing Fields * Remove repeat password on entry edit * Move show/hide password and password generator buttons into the field as actions. * Register keyboard shortcut Ctrl+H to toggle password visibility * Register keyboard shortcut Ctrl+G to open the password generator * Cleanup code and improve interactions between elements * Simplify Password Generator button layout; convert advanced mode button to toggle button * Update GUI tests * Fixes #4120 --- COPYING | 2 + .../scalable/actions/clipboard-text.svg | 1 + .../application/scalable/actions/refresh.svg | 1 + src/gui/DatabaseOpenWidget.cpp | 6 +- src/gui/DatabaseOpenWidget.ui | 43 +- src/gui/MainWindow.cpp | 2 +- src/gui/MainWindow.ui | 22 + src/gui/PasswordEdit.cpp | 132 +- src/gui/PasswordEdit.h | 21 +- src/gui/PasswordGeneratorWidget.cpp | 240 +- src/gui/PasswordGeneratorWidget.h | 15 +- src/gui/PasswordGeneratorWidget.ui | 2176 ++++++++--------- src/gui/entry/EditEntryWidget.cpp | 46 +- src/gui/entry/EditEntryWidget.h | 2 +- src/gui/entry/EditEntryWidgetMain.ui | 220 +- src/gui/masterkey/PasswordEditWidget.cpp | 36 +- src/gui/masterkey/PasswordEditWidget.h | 1 - src/gui/masterkey/PasswordEditWidget.ui | 87 +- .../wizard/NewDatabaseWizardPageMasterKey.cpp | 2 +- tests/gui/TestGui.cpp | 28 +- 20 files changed, 1459 insertions(+), 1624 deletions(-) create mode 100644 share/icons/application/scalable/actions/clipboard-text.svg create mode 100644 share/icons/application/scalable/actions/refresh.svg diff --git a/COPYING b/COPYING index 9202a0023..f06c6b225 100644 --- a/COPYING +++ b/COPYING @@ -180,6 +180,8 @@ Files: share/icons/application/scalable/categories/preferences-other.svg share/icons/application/scalable/actions/favicon-download.svg share/icons/application/scalable/actions/document-open.svg share/icons/application/scalable/actions/document-save-as.svg + share/icons/application/scalable/actions/refresh.svg + share/icons/application/scalable/actions/clipboard-text.svg Copyright: 2019 Austin Andrews License: SIL OPEN FONT LICENSE Version 1.1 Comment: Taken from Material Design icon set (https://github.com/templarian/MaterialDesign/) diff --git a/share/icons/application/scalable/actions/clipboard-text.svg b/share/icons/application/scalable/actions/clipboard-text.svg new file mode 100644 index 000000000..88e025e02 --- /dev/null +++ b/share/icons/application/scalable/actions/clipboard-text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/share/icons/application/scalable/actions/refresh.svg b/share/icons/application/scalable/actions/refresh.svg new file mode 100644 index 000000000..ebe3f16e7 --- /dev/null +++ b/share/icons/application/scalable/actions/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index f3bb502d0..c58b2df40 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -56,9 +56,6 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) m_ui->comboKeyFile->lineEdit()->addAction(m_ui->keyFileClearIcon, QLineEdit::TrailingPosition); - m_ui->buttonTogglePassword->setIcon(filePath()->onOffIcon("actions", "password-show")); - connect(m_ui->buttonTogglePassword, SIGNAL(toggled(bool)), m_ui->editPassword, SLOT(setShowPassword(bool))); - connect(m_ui->buttonTogglePassword, SIGNAL(toggled(bool)), m_ui->editPassword, SLOT(setFocus())); connect(m_ui->buttonBrowseFile, SIGNAL(clicked()), SLOT(browseKeyFile())); connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(openDatabase())); @@ -166,10 +163,10 @@ void DatabaseOpenWidget::clearForms() { if (!m_isOpeningDatabase) { m_ui->editPassword->setText(""); + m_ui->editPassword->setShowPassword(false); m_ui->comboKeyFile->clear(); m_ui->comboKeyFile->setEditText(""); m_ui->checkTouchID->setChecked(false); - m_ui->buttonTogglePassword->setChecked(false); m_db.reset(); } } @@ -195,7 +192,6 @@ void DatabaseOpenWidget::openDatabase() } m_ui->editPassword->setShowPassword(false); - m_ui->buttonTogglePassword->setChecked(false); QCoreApplication::processEvents(); m_isOpeningDatabase = true; diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui index f2cd96b6a..f89e30ffd 100644 --- a/src/gui/DatabaseOpenWidget.ui +++ b/src/gui/DatabaseOpenWidget.ui @@ -2,6 +2,14 @@ DatabaseOpenWidget + + + 0 + 0 + 580 + 410 + + Unlock KeePassXC Database @@ -157,31 +165,14 @@ - - - - - Password field - - - QLineEdit::Password - - - - - - - Toggle password visibility - - - Toggle password visibility - - - true - - - - + + + Password field + + + QLineEdit::Password + + @@ -612,8 +603,6 @@ - editPassword - buttonTogglePassword comboKeyFile buttonBrowseFile hardwareKeyLabelHelp diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 3db3e98f6..a25213980 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -451,7 +451,7 @@ MainWindow::MainWindow() connect(m_ui->actionSettings, SIGNAL(toggled(bool)), SLOT(switchToSettings(bool))); connect(m_ui->actionPasswordGenerator, SIGNAL(toggled(bool)), SLOT(switchToPasswordGen(bool))); - connect(m_ui->passwordGeneratorWidget, SIGNAL(dialogTerminated()), SLOT(closePasswordGen())); + connect(m_ui->passwordGeneratorWidget, SIGNAL(closePasswordGenerator()), SLOT(closePasswordGen())); connect(m_ui->welcomeWidget, SIGNAL(newDatabase()), SLOT(switchToNewDatabase())); connect(m_ui->welcomeWidget, SIGNAL(openDatabase()), SLOT(switchToOpenDatabase())); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index 41792986b..c68d18b83 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -186,6 +186,15 @@ + + 60 + + + 30 + + + 60 + @@ -193,6 +202,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/src/gui/PasswordEdit.cpp b/src/gui/PasswordEdit.cpp index 37b82ad8b..0cc72a995 100644 --- a/src/gui/PasswordEdit.cpp +++ b/src/gui/PasswordEdit.cpp @@ -20,14 +20,24 @@ #include "core/Config.h" #include "core/FilePath.h" +#include "gui/Application.h" #include "gui/Font.h" +#include "gui/PasswordGeneratorWidget.h" -const QColor PasswordEdit::CorrectSoFarColor = QColor(255, 205, 15); -const QColor PasswordEdit::ErrorColor = QColor(255, 125, 125); +#include +#include + +namespace +{ + const QColor CorrectSoFarColor(255, 205, 15); + const QColor CorrectSoFarColorDark(115, 104, 46); + const QColor ErrorColor(255, 125, 125); + const QColor ErrorColorDark(128, 45, 45); + +} // namespace PasswordEdit::PasswordEdit(QWidget* parent) : QLineEdit(parent) - , m_basePasswordEdit(nullptr) { const QIcon errorIcon = filePath()->icon("status", "dialog-error"); m_errorAction = addAction(errorIcon, QLineEdit::TrailingPosition); @@ -40,70 +50,122 @@ PasswordEdit::PasswordEdit(QWidget* parent) m_correctAction->setToolTip(tr("Passwords match so far")); setEchoMode(QLineEdit::Password); - updateStylesheet(); // use a monospace font for the password field QFont passwordFont = Font::fixedFont(); passwordFont.setLetterSpacing(QFont::PercentageSpacing, 110); setFont(passwordFont); + + m_toggleVisibleAction = new QAction( + filePath()->icon("actions", "password-show-off"), + tr("Toggle Password (%1)").arg(QKeySequence(Qt::CTRL + Qt::Key_H).toString(QKeySequence::NativeText)), + nullptr); + m_toggleVisibleAction->setCheckable(true); + m_toggleVisibleAction->setShortcut(Qt::CTRL + Qt::Key_H); + m_toggleVisibleAction->setShortcutContext(Qt::WidgetShortcut); + addAction(m_toggleVisibleAction, QLineEdit::TrailingPosition); + connect(m_toggleVisibleAction, &QAction::triggered, this, &PasswordEdit::setShowPassword); + + m_passwordGeneratorAction = new QAction( + filePath()->icon("actions", "password-generator"), + tr("Generate Password (%1)").arg(QKeySequence(Qt::CTRL + Qt::Key_G).toString(QKeySequence::NativeText)), + nullptr); + m_passwordGeneratorAction->setShortcut(Qt::CTRL + Qt::Key_G); + m_passwordGeneratorAction->setShortcutContext(Qt::WidgetShortcut); + addAction(m_passwordGeneratorAction, QLineEdit::TrailingPosition); + m_passwordGeneratorAction->setVisible(false); } -void PasswordEdit::enableVerifyMode(PasswordEdit* basePasswordEdit) +void PasswordEdit::setRepeatPartner(PasswordEdit* repeatEdit) { - m_basePasswordEdit = basePasswordEdit; + m_repeatPasswordEdit = repeatEdit; + m_repeatPasswordEdit->setParentPasswordEdit(this); - updateStylesheet(); + connect(this, SIGNAL(textChanged(QString)), m_repeatPasswordEdit, SLOT(autocompletePassword(QString))); + connect(this, SIGNAL(textChanged(QString)), m_repeatPasswordEdit, SLOT(updateRepeatStatus())); + connect(m_repeatPasswordEdit, SIGNAL(textChanged(QString)), m_repeatPasswordEdit, SLOT(updateRepeatStatus())); +} - connect(m_basePasswordEdit, SIGNAL(textChanged(QString)), SLOT(autocompletePassword(QString))); - connect(m_basePasswordEdit, SIGNAL(textChanged(QString)), SLOT(updateStylesheet())); - connect(this, SIGNAL(textChanged(QString)), SLOT(updateStylesheet())); +void PasswordEdit::setParentPasswordEdit(PasswordEdit* parent) +{ + m_parentPasswordEdit = parent; + // Hide actions + m_toggleVisibleAction->setVisible(false); + m_passwordGeneratorAction->setVisible(false); +} - connect(m_basePasswordEdit, SIGNAL(showPasswordChanged(bool)), SLOT(setShowPassword(bool))); +void PasswordEdit::enablePasswordGenerator(bool signalOnly) +{ + disconnect(m_passwordGeneratorAction); + m_passwordGeneratorAction->setVisible(true); + + if (signalOnly) { + connect(m_passwordGeneratorAction, &QAction::triggered, this, &PasswordEdit::togglePasswordGenerator); + } else { + connect(m_passwordGeneratorAction, &QAction::triggered, this, &PasswordEdit::popupPasswordGenerator); + } } void PasswordEdit::setShowPassword(bool show) { setEchoMode(show ? QLineEdit::Normal : QLineEdit::Password); - // if I have a parent, I'm the child - if (m_basePasswordEdit) { + m_toggleVisibleAction->setIcon(filePath()->icon("actions", show ? "password-show-on" : "password-show-off")); + m_toggleVisibleAction->setChecked(show); + + if (m_repeatPasswordEdit) { + m_repeatPasswordEdit->setEchoMode(show ? QLineEdit::Normal : QLineEdit::Password); if (config()->get("security/passwordsrepeat").toBool()) { - setEnabled(!show); - setReadOnly(show); - setText(m_basePasswordEdit->text()); + m_repeatPasswordEdit->setEnabled(!show); + m_repeatPasswordEdit->setText(text()); } else { - // This fix a bug when the QLineEdit is disabled while switching config - if (!isEnabled()) { - setEnabled(true); - setReadOnly(false); - } + m_repeatPasswordEdit->setEnabled(true); } } - updateStylesheet(); - emit showPasswordChanged(show); } bool PasswordEdit::isPasswordVisible() const { - return isEnabled(); + return echoMode() == QLineEdit::Normal; } -bool PasswordEdit::passwordsEqual() const +void PasswordEdit::popupPasswordGenerator() { - return text() == m_basePasswordEdit->text(); + auto pwGenerator = new PasswordGeneratorWidget(); + QDialog pwDialog(this); + pwDialog.setWindowTitle(tr("Generate Password")); + auto layout = new QVBoxLayout(); + pwDialog.setLayout(layout); + layout->addWidget(pwGenerator); + + pwGenerator->setStandaloneMode(false); + pwGenerator->setPasswordVisible(isPasswordVisible()); + + connect(pwGenerator, SIGNAL(closePasswordGenerator()), &pwDialog, SLOT(close())); + connect(pwGenerator, SIGNAL(appliedPassword(QString)), SLOT(setText(QString))); + if (m_repeatPasswordEdit) { + connect(pwGenerator, SIGNAL(appliedPassword(QString)), m_repeatPasswordEdit, SLOT(setText(QString))); + } + + pwDialog.exec(); } -void PasswordEdit::updateStylesheet() +void PasswordEdit::updateRepeatStatus() { - const QString stylesheetTemplate("QLineEdit { background: %1; }"); + static const auto stylesheetTemplate = QStringLiteral("QLineEdit { background: %1; }"); + if (!m_parentPasswordEdit) { + return; + } - if (m_basePasswordEdit && !passwordsEqual()) { - bool isCorrect = true; - if (m_basePasswordEdit->text().startsWith(text())) { - setStyleSheet(stylesheetTemplate.arg(CorrectSoFarColor.name())); - } else { - setStyleSheet(stylesheetTemplate.arg(ErrorColor.name())); - isCorrect = false; + const auto otherPassword = m_parentPasswordEdit->text(); + const auto password = text(); + if (otherPassword != password) { + bool isCorrect = false; + QColor color = kpxcApp->isDarkTheme() ? ErrorColorDark : ErrorColor; + if (!password.isEmpty() && otherPassword.startsWith(password)) { + color = kpxcApp->isDarkTheme() ? CorrectSoFarColorDark : CorrectSoFarColor; + isCorrect = true; } + setStyleSheet(stylesheetTemplate.arg(color.name())); m_correctAction->setVisible(isCorrect); m_errorAction->setVisible(!isCorrect); } else { diff --git a/src/gui/PasswordEdit.h b/src/gui/PasswordEdit.h index b6e74ed00..365f4cdb1 100644 --- a/src/gui/PasswordEdit.h +++ b/src/gui/PasswordEdit.h @@ -28,29 +28,32 @@ class PasswordEdit : public QLineEdit Q_OBJECT public: - static const QColor CorrectSoFarColor; - static const QColor ErrorColor; - explicit PasswordEdit(QWidget* parent = nullptr); - void enableVerifyMode(PasswordEdit* baseEdit); + void enablePasswordGenerator(bool signalOnly = false); + void setRepeatPartner(PasswordEdit* repeatEdit); bool isPasswordVisible() const; public slots: void setShowPassword(bool show); + void updateRepeatStatus(); signals: - void showPasswordChanged(bool show); + void togglePasswordGenerator(); private slots: - void updateStylesheet(); void autocompletePassword(const QString& password); + void popupPasswordGenerator(); + void setParentPasswordEdit(PasswordEdit* parent); private: - bool passwordsEqual() const; - QPointer m_errorAction; QPointer m_correctAction; - QPointer m_basePasswordEdit; + QPointer m_toggleVisibleAction; + QPointer m_passwordGeneratorAction; + QPointer m_repeatPasswordEdit; + QPointer m_parentPasswordEdit; + bool m_sendGeneratorSignal = false; + bool m_isRepeatPartner = false; }; #endif // KEEPASSX_PASSWORDEDIT_H diff --git a/src/gui/PasswordGeneratorWidget.cpp b/src/gui/PasswordGeneratorWidget.cpp index 7d6f05d41..bfdbfe304 100644 --- a/src/gui/PasswordGeneratorWidget.cpp +++ b/src/gui/PasswordGeneratorWidget.cpp @@ -27,36 +27,38 @@ #include "core/FilePath.h" #include "core/PasswordGenerator.h" #include "core/PasswordHealth.h" +#include "gui/Application.h" #include "gui/Clipboard.h" -#include "gui/osutils/OSUtils.h" PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent) : QWidget(parent) - , m_updatingSpinBox(false) , m_passwordGenerator(new PasswordGenerator()) , m_dicewareGenerator(new PassphraseGenerator()) , m_ui(new Ui::PasswordGeneratorWidget()) { m_ui->setupUi(this); - m_ui->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show")); + m_ui->buttonGenerate->setIcon(filePath()->icon("actions", "refresh")); + m_ui->buttonGenerate->setToolTip( + tr("Regenerate password (%1)").arg(m_ui->buttonGenerate->shortcut().toString(QKeySequence::NativeText))); + m_ui->buttonCopy->setIcon(filePath()->icon("actions", "clipboard-text")); + m_ui->buttonClose->setShortcut(Qt::Key_Escape); connect(m_ui->editNewPassword, SIGNAL(textChanged(QString)), SLOT(updateButtonsEnabled(QString))); connect(m_ui->editNewPassword, SIGNAL(textChanged(QString)), SLOT(updatePasswordStrength(QString))); - connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), SLOT(setPasswordVisible(bool))); - connect(m_ui->buttonSimpleMode, SIGNAL(clicked()), SLOT(selectSimpleMode())); - connect(m_ui->buttonAdvancedMode, SIGNAL(clicked()), SLOT(selectAdvancedMode())); + connect(m_ui->buttonAdvancedMode, SIGNAL(toggled(bool)), SLOT(setAdvancedMode(bool))); connect(m_ui->buttonAddHex, SIGNAL(clicked()), SLOT(excludeHexChars())); connect(m_ui->editExcludedChars, SIGNAL(textChanged(QString)), SLOT(updateGenerator())); connect(m_ui->buttonApply, SIGNAL(clicked()), SLOT(applyPassword())); connect(m_ui->buttonCopy, SIGNAL(clicked()), SLOT(copyPassword())); connect(m_ui->buttonGenerate, SIGNAL(clicked()), SLOT(regeneratePassword())); + connect(m_ui->buttonClose, SIGNAL(clicked()), SIGNAL(closePasswordGenerator())); - connect(m_ui->sliderLength, SIGNAL(valueChanged(int)), SLOT(passwordSliderMoved())); - connect(m_ui->spinBoxLength, SIGNAL(valueChanged(int)), SLOT(passwordSpinBoxChanged())); + connect(m_ui->sliderLength, SIGNAL(valueChanged(int)), SLOT(passwordLengthChanged(int))); + connect(m_ui->spinBoxLength, SIGNAL(valueChanged(int)), SLOT(passwordLengthChanged(int))); - connect(m_ui->sliderWordCount, SIGNAL(valueChanged(int)), SLOT(dicewareSliderMoved())); - connect(m_ui->spinBoxWordCount, SIGNAL(valueChanged(int)), SLOT(dicewareSpinBoxChanged())); + connect(m_ui->sliderWordCount, SIGNAL(valueChanged(int)), SLOT(passphraseLengthChanged(int))); + connect(m_ui->spinBoxWordCount, SIGNAL(valueChanged(int)), SLOT(passphraseLengthChanged(int))); connect(m_ui->editWordSeparator, SIGNAL(textChanged(QString)), SLOT(updateGenerator())); connect(m_ui->comboBoxWordList, SIGNAL(currentIndexChanged(int)), SLOT(updateGenerator())); @@ -100,12 +102,6 @@ PasswordGeneratorWidget::~PasswordGeneratorWidget() { } -void PasswordGeneratorWidget::showEvent(QShowEvent* event) -{ - QWidget::showEvent(event); - reset(); -} - void PasswordGeneratorWidget::loadSettings() { // Password config @@ -118,19 +114,13 @@ void PasswordGeneratorWidget::loadSettings() config()->get("generator/SpecialChars", PasswordGenerator::DefaultSpecial).toBool()); m_ui->checkBoxNumbersAdv->setChecked( config()->get("generator/Numbers", PasswordGenerator::DefaultNumbers).toBool()); - m_ui->advancedBar->setVisible( - config()->get("generator/AdvancedMode", PasswordGenerator::DefaultAdvancedMode).toBool()); - m_ui->excludedChars->setVisible( - config()->get("generator/AdvancedMode", PasswordGenerator::DefaultAdvancedMode).toBool()); - m_ui->checkBoxExcludeAlike->setVisible( - config()->get("generator/AdvancedMode", PasswordGenerator::DefaultAdvancedMode).toBool()); - m_ui->checkBoxEnsureEvery->setVisible( - config()->get("generator/AdvancedMode", PasswordGenerator::DefaultAdvancedMode).toBool()); m_ui->editExcludedChars->setText( config()->get("generator/ExcludedChars", PasswordGenerator::DefaultExcludedChars).toString()); - m_ui->simpleBar->setVisible( - !(config()->get("generator/AdvancedMode", PasswordGenerator::DefaultAdvancedMode).toBool())); + m_ui->buttonAdvancedMode->setChecked( + config()->get("generator/AdvancedMode", PasswordGenerator::DefaultAdvancedMode).toBool()); + setAdvancedMode(m_ui->buttonAdvancedMode->isChecked()); + m_ui->checkBoxBraces->setChecked(config()->get("generator/Braces", PasswordGenerator::DefaultBraces).toBool()); m_ui->checkBoxQuotes->setChecked(config()->get("generator/Quotes", PasswordGenerator::DefaultQuotes).toBool()); m_ui->checkBoxPunctuation->setChecked( @@ -174,7 +164,7 @@ void PasswordGeneratorWidget::saveSettings() config()->set("generator/Numbers", m_ui->checkBoxNumbersAdv->isChecked()); config()->set("generator/EASCII", m_ui->checkBoxExtASCIIAdv->isChecked()); } - config()->set("generator/AdvancedMode", m_ui->advancedBar->isVisible()); + config()->set("generator/AdvancedMode", m_ui->buttonAdvancedMode->isChecked()); config()->set("generator/SpecialChars", m_ui->checkBoxSpecialChars->isChecked()); config()->set("generator/Braces", m_ui->checkBoxBraces->isChecked()); config()->set("generator/Punctuation", m_ui->checkBoxPunctuation->isChecked()); @@ -215,10 +205,10 @@ void PasswordGeneratorWidget::setStandaloneMode(bool standalone) { m_standalone = standalone; if (standalone) { - m_ui->buttonApply->setText(tr("Close")); + m_ui->buttonApply->setVisible(false); setPasswordVisible(true); } else { - m_ui->buttonApply->setText(tr("Accept")); + m_ui->buttonApply->setVisible(true); } } @@ -230,7 +220,7 @@ QString PasswordGeneratorWidget::getGeneratedPassword() void PasswordGeneratorWidget::keyPressEvent(QKeyEvent* e) { if (e->key() == Qt::Key_Escape && m_standalone) { - emit dialogTerminated(); + emit closePasswordGenerator(); } else { e->ignore(); } @@ -280,7 +270,7 @@ void PasswordGeneratorWidget::applyPassword() { saveSettings(); emit appliedPassword(m_ui->editNewPassword->text()); - emit dialogTerminated(); + emit closePasswordGenerator(); } void PasswordGeneratorWidget::copyPassword() @@ -288,43 +278,30 @@ void PasswordGeneratorWidget::copyPassword() clipboard()->setText(m_ui->editNewPassword->text()); } -void PasswordGeneratorWidget::passwordSliderMoved() +void PasswordGeneratorWidget::passwordLengthChanged(int length) { - if (m_updatingSpinBox) { - return; - } + m_ui->spinBoxLength->blockSignals(true); + m_ui->sliderLength->blockSignals(true); - m_ui->spinBoxLength->setValue(m_ui->sliderLength->value()); + m_ui->spinBoxLength->setValue(length); + m_ui->sliderLength->setValue(length); + + m_ui->spinBoxLength->blockSignals(false); + m_ui->sliderLength->blockSignals(false); updateGenerator(); } -void PasswordGeneratorWidget::passwordSpinBoxChanged() +void PasswordGeneratorWidget::passphraseLengthChanged(int length) { - if (m_updatingSpinBox) { - return; - } + m_ui->spinBoxWordCount->blockSignals(true); + m_ui->sliderWordCount->blockSignals(true); - // Interlock so that we don't update twice - this causes issues as the spinbox can go higher than slider - m_updatingSpinBox = true; + m_ui->spinBoxWordCount->setValue(length); + m_ui->sliderWordCount->setValue(length); - m_ui->sliderLength->setValue(m_ui->spinBoxLength->value()); - - m_updatingSpinBox = false; - - updateGenerator(); -} - -void PasswordGeneratorWidget::dicewareSliderMoved() -{ - m_ui->spinBoxWordCount->setValue(m_ui->sliderWordCount->value()); - - updateGenerator(); -} - -void PasswordGeneratorWidget::dicewareSpinBoxChanged() -{ - m_ui->sliderWordCount->setValue(m_ui->spinBoxWordCount->value()); + m_ui->spinBoxWordCount->blockSignals(false); + m_ui->sliderWordCount->blockSignals(false); updateGenerator(); } @@ -332,49 +309,46 @@ void PasswordGeneratorWidget::dicewareSpinBoxChanged() void PasswordGeneratorWidget::setPasswordVisible(bool visible) { m_ui->editNewPassword->setShowPassword(visible); - bool blockSignals = m_ui->togglePasswordButton->blockSignals(true); - m_ui->togglePasswordButton->setChecked(visible); - m_ui->togglePasswordButton->blockSignals(blockSignals); } bool PasswordGeneratorWidget::isPasswordVisible() const { - return m_ui->togglePasswordButton->isChecked(); + return m_ui->editNewPassword->isPasswordVisible(); } -void PasswordGeneratorWidget::selectSimpleMode() +void PasswordGeneratorWidget::setAdvancedMode(bool state) { - m_ui->advancedBar->hide(); - m_ui->excludedChars->hide(); - m_ui->checkBoxExcludeAlike->hide(); - m_ui->checkBoxEnsureEvery->hide(); - m_ui->checkBoxUpper->setChecked(m_ui->checkBoxUpperAdv->isChecked()); - m_ui->checkBoxLower->setChecked(m_ui->checkBoxLowerAdv->isChecked()); - m_ui->checkBoxNumbers->setChecked(m_ui->checkBoxNumbersAdv->isChecked()); - m_ui->checkBoxSpecialChars->setChecked(m_ui->checkBoxBraces->isChecked() | m_ui->checkBoxPunctuation->isChecked() - | m_ui->checkBoxQuotes->isChecked() | m_ui->checkBoxMath->isChecked() - | m_ui->checkBoxDashes->isChecked() | m_ui->checkBoxLogograms->isChecked()); - m_ui->checkBoxExtASCII->setChecked(m_ui->checkBoxExtASCIIAdv->isChecked()); - m_ui->simpleBar->show(); -} - -void PasswordGeneratorWidget::selectAdvancedMode() -{ - m_ui->simpleBar->hide(); - m_ui->checkBoxUpperAdv->setChecked(m_ui->checkBoxUpper->isChecked()); - m_ui->checkBoxLowerAdv->setChecked(m_ui->checkBoxLower->isChecked()); - m_ui->checkBoxNumbersAdv->setChecked(m_ui->checkBoxNumbers->isChecked()); - m_ui->checkBoxBraces->setChecked(m_ui->checkBoxSpecialChars->isChecked()); - m_ui->checkBoxPunctuation->setChecked(m_ui->checkBoxSpecialChars->isChecked()); - m_ui->checkBoxQuotes->setChecked(m_ui->checkBoxSpecialChars->isChecked()); - m_ui->checkBoxMath->setChecked(m_ui->checkBoxSpecialChars->isChecked()); - m_ui->checkBoxDashes->setChecked(m_ui->checkBoxSpecialChars->isChecked()); - m_ui->checkBoxLogograms->setChecked(m_ui->checkBoxSpecialChars->isChecked()); - m_ui->checkBoxExtASCIIAdv->setChecked(m_ui->checkBoxExtASCII->isChecked()); - m_ui->advancedBar->show(); - m_ui->excludedChars->show(); - m_ui->checkBoxExcludeAlike->show(); - m_ui->checkBoxEnsureEvery->show(); + if (state) { + m_ui->simpleBar->hide(); + m_ui->checkBoxUpperAdv->setChecked(m_ui->checkBoxUpper->isChecked()); + m_ui->checkBoxLowerAdv->setChecked(m_ui->checkBoxLower->isChecked()); + m_ui->checkBoxNumbersAdv->setChecked(m_ui->checkBoxNumbers->isChecked()); + m_ui->checkBoxBraces->setChecked(m_ui->checkBoxSpecialChars->isChecked()); + m_ui->checkBoxPunctuation->setChecked(m_ui->checkBoxSpecialChars->isChecked()); + m_ui->checkBoxQuotes->setChecked(m_ui->checkBoxSpecialChars->isChecked()); + m_ui->checkBoxMath->setChecked(m_ui->checkBoxSpecialChars->isChecked()); + m_ui->checkBoxDashes->setChecked(m_ui->checkBoxSpecialChars->isChecked()); + m_ui->checkBoxLogograms->setChecked(m_ui->checkBoxSpecialChars->isChecked()); + m_ui->checkBoxExtASCIIAdv->setChecked(m_ui->checkBoxExtASCII->isChecked()); + m_ui->advancedBar->show(); + m_ui->excludedChars->show(); + m_ui->checkBoxExcludeAlike->show(); + m_ui->checkBoxEnsureEvery->show(); + } else { + m_ui->advancedBar->hide(); + m_ui->excludedChars->hide(); + m_ui->checkBoxExcludeAlike->hide(); + m_ui->checkBoxEnsureEvery->hide(); + m_ui->checkBoxUpper->setChecked(m_ui->checkBoxUpperAdv->isChecked()); + m_ui->checkBoxLower->setChecked(m_ui->checkBoxLowerAdv->isChecked()); + m_ui->checkBoxNumbers->setChecked(m_ui->checkBoxNumbersAdv->isChecked()); + m_ui->checkBoxSpecialChars->setChecked( + m_ui->checkBoxBraces->isChecked() | m_ui->checkBoxPunctuation->isChecked() + | m_ui->checkBoxQuotes->isChecked() | m_ui->checkBoxMath->isChecked() | m_ui->checkBoxDashes->isChecked() + | m_ui->checkBoxLogograms->isChecked()); + m_ui->checkBoxExtASCII->setChecked(m_ui->checkBoxExtASCIIAdv->isChecked()); + m_ui->simpleBar->show(); + } } void PasswordGeneratorWidget::excludeHexChars() @@ -392,7 +366,7 @@ void PasswordGeneratorWidget::colorStrengthIndicator(const PasswordHealth& healt // Set the color and background based on entropy QList qualityColors; - if (osUtils->isDarkMode()) { + if (kpxcApp->isDarkTheme()) { qualityColors << QStringLiteral("#C43F31") << QStringLiteral("#DB9837") << QStringLiteral("#608A22") << QStringLiteral("#1F8023"); } else { @@ -496,12 +470,14 @@ PasswordGenerator::GeneratorFlags PasswordGeneratorWidget::generatorFlags() { PasswordGenerator::GeneratorFlags flags; - if (m_ui->checkBoxExcludeAlike->isChecked()) { - flags |= PasswordGenerator::ExcludeLookAlike; - } + if (m_ui->buttonAdvancedMode->isChecked()) { + if (m_ui->checkBoxExcludeAlike->isChecked()) { + flags |= PasswordGenerator::ExcludeLookAlike; + } - if (m_ui->checkBoxEnsureEvery->isChecked()) { - flags |= PasswordGenerator::CharFromEveryGroup; + if (m_ui->checkBoxEnsureEvery->isChecked()) { + flags |= PasswordGenerator::CharFromEveryGroup; + } } return flags; @@ -510,62 +486,52 @@ PasswordGenerator::GeneratorFlags PasswordGeneratorWidget::generatorFlags() void PasswordGeneratorWidget::updateGenerator() { if (m_ui->tabWidget->currentIndex() == Password) { - PasswordGenerator::CharClasses classes = charClasses(); - PasswordGenerator::GeneratorFlags flags = generatorFlags(); + auto classes = charClasses(); + auto flags = generatorFlags(); - int minLength = 0; + int length = 0; if (flags.testFlag(PasswordGenerator::CharFromEveryGroup)) { if (classes.testFlag(PasswordGenerator::LowerLetters)) { - minLength++; + ++length; } if (classes.testFlag(PasswordGenerator::UpperLetters)) { - minLength++; + ++length; } if (classes.testFlag(PasswordGenerator::Numbers)) { - minLength++; + ++length; } if (classes.testFlag(PasswordGenerator::Braces)) { - minLength++; + ++length; } if (classes.testFlag(PasswordGenerator::Punctuation)) { - minLength++; + ++length; } if (classes.testFlag(PasswordGenerator::Quotes)) { - minLength++; + ++length; } if (classes.testFlag(PasswordGenerator::Dashes)) { - minLength++; + ++length; } if (classes.testFlag(PasswordGenerator::Math)) { - minLength++; + ++length; } if (classes.testFlag(PasswordGenerator::Logograms)) { - minLength++; + ++length; } if (classes.testFlag(PasswordGenerator::EASCII)) { - minLength++; + ++length; } } - minLength = qMax(minLength, 1); - if (m_ui->spinBoxLength->value() < minLength) { - m_updatingSpinBox = true; - m_ui->spinBoxLength->setValue(minLength); - m_ui->sliderLength->setValue(minLength); - m_updatingSpinBox = false; - } - - m_ui->spinBoxLength->setMinimum(minLength); - m_ui->sliderLength->setMinimum(minLength); - - m_passwordGenerator->setLength(m_ui->spinBoxLength->value()); + length = qMax(length, m_ui->spinBoxLength->value()); + m_passwordGenerator->setLength(length); m_passwordGenerator->setCharClasses(classes); - if (m_ui->simpleBar->isVisible()) { - m_passwordGenerator->setExcludedChars(""); - } else { - m_passwordGenerator->setExcludedChars(m_ui->editExcludedChars->text()); - } m_passwordGenerator->setFlags(flags); + if (m_ui->buttonAdvancedMode->isChecked()) { + m_passwordGenerator->setExcludedChars(m_ui->editExcludedChars->text()); + } else { + m_passwordGenerator->setExcludedChars(""); + } if (m_passwordGenerator->isValid()) { m_ui->buttonGenerate->setEnabled(true); @@ -573,21 +539,9 @@ void PasswordGeneratorWidget::updateGenerator() m_ui->buttonGenerate->setEnabled(false); } } else { - int minWordCount = 1; - - if (m_ui->spinBoxWordCount->value() < minWordCount) { - m_updatingSpinBox = true; - m_ui->spinBoxWordCount->setValue(minWordCount); - m_ui->sliderWordCount->setValue(minWordCount); - m_updatingSpinBox = false; - } - m_dicewareGenerator->setWordCase( static_cast(m_ui->wordCaseComboBox->currentData().toInt())); - m_ui->spinBoxWordCount->setMinimum(minWordCount); - m_ui->sliderWordCount->setMinimum(minWordCount); - m_dicewareGenerator->setWordCount(m_ui->spinBoxWordCount->value()); if (!m_ui->comboBoxWordList->currentText().isEmpty()) { QString path = filePath()->wordlistPath(m_ui->comboBoxWordList->currentText()); diff --git a/src/gui/PasswordGeneratorWidget.h b/src/gui/PasswordGeneratorWidget.h index eba7f815f..7e9390a9f 100644 --- a/src/gui/PasswordGeneratorWidget.h +++ b/src/gui/PasswordGeneratorWidget.h @@ -54,9 +54,6 @@ public: QString getGeneratedPassword(); bool isPasswordVisible() const; -protected: - void showEvent(QShowEvent* event) override; - public slots: void regeneratePassword(); void applyPassword(); @@ -65,25 +62,21 @@ public slots: signals: void appliedPassword(const QString& password); - void dialogTerminated(); + void closePasswordGenerator(); private slots: void updateButtonsEnabled(const QString& password); void updatePasswordStrength(const QString& password); - void selectSimpleMode(); - void selectAdvancedMode(); + void setAdvancedMode(bool state); void excludeHexChars(); - void passwordSliderMoved(); - void passwordSpinBoxChanged(); - void dicewareSliderMoved(); - void dicewareSpinBoxChanged(); + void passwordLengthChanged(int length); + void passphraseLengthChanged(int length); void colorStrengthIndicator(const PasswordHealth& health); void updateGenerator(); private: - bool m_updatingSpinBox; bool m_standalone = false; PasswordGenerator::CharClasses charClasses(); diff --git a/src/gui/PasswordGeneratorWidget.ui b/src/gui/PasswordGeneratorWidget.ui index 38af84b75..62d96a2a1 100644 --- a/src/gui/PasswordGeneratorWidget.ui +++ b/src/gui/PasswordGeneratorWidget.ui @@ -6,35 +6,106 @@ 0 0 - 716 - 468 + 622 + 404 - - - 0 - 0 - - - + Generate Password - - - QLayout::SetMinimumSize - + - - QLayout::SetMinimumSize - - - 0 - - - 0 - - + + + + + + + 70 + 0 + + + + + 16777215 + 30 + + + + strength + + + Qt::PlainText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 3 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 70 + 0 + + + + entropy + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + 3 + + + + + + + + + + 0 + 0 + + + + + 350 + 0 + + + + Generated password + + + + @@ -84,99 +155,954 @@ QProgressBar::chunk { - - - - Password: - - - editNewPassword - - - - - - - - - - 70 - 0 - - - - - 16777215 - 30 - - - - strength - - - Qt::PlainText - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - 3 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 70 - 0 - - - - entropy - - - Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing - - - 3 - - - - - - - - - Generated password - - - 999 - - - - - - Toggle password visibility + + + Copy password - + Copy password - + + Ctrl+C + + + + + + + Regenerate password + + + Ctrl+R + + + + + + + + + + 0 + 0 + + + + QTabWidget::North + + + 0 + + + + Password + + + + + + + 580 + 0 + + + + Qt::TabFocus + + + Character Types + + + + QLayout::SetMinimumSize + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 40 + 25 + + + + Qt::TabFocus + + + Upper-case letters + + + Upper-case letters + + + + + + A-Z + + + true + + + optionButtons + + + + + + + + 40 + 25 + + + + Qt::TabFocus + + + Lower-case letters + + + Lower-case letters + + + + + + a-z + + + true + + + optionButtons + + + + + + + + 40 + 25 + + + + Qt::TabFocus + + + Numbers + + + Numbers + + + + + + 0-9 + + + true + + + optionButtons + + + + + + + true + + + + 60 + 25 + + + + Qt::TabFocus + + + Special characters + + + Special characters + + + /*_& ... + + + true + + + optionButtons + + + + + + + + 105 + 25 + + + + Qt::TabFocus + + + Extended ASCII + + + Extended ASCII + + + ExtendedASCII + + + true + + + optionButtons + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QLayout::SetMinimumSize + + + + + + 40 + 25 + + + + Qt::TabFocus + + + Upper-case letters + + + Upper-case letters + + + A-Z + + + true + + + optionButtons + + + + + + + + 40 + 25 + + + + Qt::TabFocus + + + Lower-case letters + + + Lower-case letters + + + a-z + + + true + + + optionButtons + + + + + + + + + QLayout::SetMinimumSize + + + + + + 40 + 25 + + + + Qt::TabFocus + + + Numbers + + + 0-9 + + + true + + + optionButtons + + + + + + + + 40 + 25 + + + + Qt::TabFocus + + + Braces + + + Braces + + + {[( + + + true + + + optionButtons + + + + + + + + + QLayout::SetMinimumSize + + + + + + 40 + 25 + + + + Qt::TabFocus + + + Punctuation + + + Punctuation + + + .,:; + + + true + + + optionButtons + + + + + + + + 40 + 25 + + + + Qt::TabFocus + + + Quotes + + + " ' + + + true + + + optionButtons + + + + + + + + + QLayout::SetMinimumSize + + + + + + 60 + 25 + + + + Qt::TabFocus + + + Math Symbols + + + Math Symbols + + + <*+!?= + + + true + + + optionButtons + + + + + + + + 60 + 25 + + + + Qt::TabFocus + + + Dashes and Slashes + + + Dashes and Slashes + + + \_|-/ + + + true + + + optionButtons + + + + + + + + + QLayout::SetMinimumSize + + + + + + 105 + 25 + + + + Qt::TabFocus + + + Logograms + + + Logograms + + + #$%&&@^`~ + + + true + + + optionButtons + + + + + + + + 105 + 25 + + + + Qt::TabFocus + + + Extended ASCII + + + ExtendedASCII + + + true + + + optionButtons + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Do not include: + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + Character set to exclude from generated password + + + Excluded characters + + + true + + + + + + + Qt::TabFocus + + + Add non-hex letters to "do not include" list + + + Hex Passwords + + + Hex + + + + + + + + + + Excluded characters: "0", "1", "l", "I", "O", "|", "﹒" + + + Exclude look-alike characters + + + optionButtons + + + + + + + Pick characters from every group + + + optionButtons + + + + + + + + + + 15 + + + QLayout::SetMinimumSize + + + 0 + + + + + &Length: + + + spinBoxLength + + + + + + + Password length + + + 1 + + + 128 + + + 20 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 8 + + + + + + + Password length + + + 1 + + + 128 + + + 20 + + + + + + + Qt::TabFocus + + + Switch to advanced mode + + + Advanced + + + true + + + optionButtons + + + + + + + + + + Passphrase + + + + + + + + + + Word Case: + + + + + + + + 0 + 0 + + + + + + + + Word Separator: + + + + + + + + 0 + 0 + + + + Wordlist: + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QLayout::SetMinimumSize + + + + + 1 + + + 40 + + + 6 + + + 6 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 8 + + + + + + + 1 + + + 100 + + + 6 + + + + + + + + + Word Count: + + + spinBoxLength + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + Apply Password + + true @@ -184,1021 +1110,13 @@ QProgressBar::chunk { - - - QLayout::SetMinimumSize - - - - - - 0 - 0 - - - - QTabWidget::North - - - QTabWidget::Rounded - - - 0 - - - - Password - - - - - - QLayout::SetMinimumSize - - - - - - 580 - 0 - - - - Character Types - - - - QLayout::SetMinimumSize - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QLayout::SetMinimumSize - - - - - - 0 - 25 - - - - Qt::StrongFocus - - - Upper-case letters - - - Upper-case letters - - - - - - A-Z - - - true - - - optionButtons - - - - - - - - 0 - 25 - - - - Qt::StrongFocus - - - Lower-case letters - - - Lower-case letters - - - - - - a-z - - - true - - - optionButtons - - - - - - - - 0 - 25 - - - - Qt::StrongFocus - - - Numbers - - - Numbers - - - - - - 0-9 - - - true - - - optionButtons - - - - - - - true - - - - 0 - 25 - - - - Qt::StrongFocus - - - Special characters - - - Special characters - - - /*_& ... - - - true - - - optionButtons - - - - - - - - 0 - 25 - - - - - 16777215 - 16777215 - - - - Qt::StrongFocus - - - Extended ASCII - - - Extended ASCII - - - ExtendedASCII - - - true - - - optionButtons - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 25 - - - - Switch to advanced mode - - - Advanced - - - optionButtons - - - - - - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QLayout::SetMinimumSize - - - - - - 40 - 25 - - - - Qt::StrongFocus - - - Upper-case letters - - - Upper-case letters - - - A-Z - - - true - - - optionButtons - - - - - - - - 40 - 25 - - - - Qt::StrongFocus - - - Lower-case letters - - - Lower-case letters - - - a-z - - - true - - - optionButtons - - - - - - - - - QLayout::SetMinimumSize - - - - - - 40 - 25 - - - - Qt::StrongFocus - - - Numbers - - - 0-9 - - - true - - - optionButtons - - - - - - - - 40 - 25 - - - - Qt::StrongFocus - - - Braces - - - Braces - - - {[( - - - true - - - optionButtons - - - - - - - - - QLayout::SetMinimumSize - - - - - - 35 - 25 - - - - Qt::StrongFocus - - - Punctuation - - - Punctuation - - - .,:; - - - true - - - optionButtons - - - - - - - - 35 - 25 - - - - Qt::StrongFocus - - - Quotes - - - " ' - - - true - - - optionButtons - - - - - - - - - QLayout::SetMinimumSize - - - - - - 60 - 25 - - - - Qt::StrongFocus - - - Math Symbols - - - Math Symbols - - - <*+!?= - - - true - - - optionButtons - - - - - - - - 60 - 25 - - - - Qt::StrongFocus - - - Dashes and Slashes - - - Dashes and Slashes - - - \_|-/ - - - true - - - optionButtons - - - - - - - - - QLayout::SetMinimumSize - - - - - - 105 - 25 - - - - Qt::StrongFocus - - - Logograms - - - Logograms - - - #$%&&@^`~ - - - true - - - optionButtons - - - - - - - - 105 - 25 - - - - Qt::StrongFocus - - - Extended ASCII - - - ExtendedASCII - - - true - - - optionButtons - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - QLayout::SetMinimumSize - - - - - - 0 - 25 - - - - Switch to simple mode - - - Simple - - - optionButtons - - - - - - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - Character set to exclude from generated password - - - Excluded characters - - - true - - - - - - - Do not include: - - - - - - - - 0 - 25 - - - - Add non-hex letters to "do not include" list - - - Hex Passwords - - - Hex - - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - Excluded characters: "0", "1", "l", "I", "O", "|", "﹒" - - - Exclude look-alike characters - - - optionButtons - - - - - - - Pick characters from every group - - - optionButtons - - - - - - - - - - - - 15 - - - QLayout::SetMinimumSize - - - 6 - - - - - &Length: - - - spinBoxLength - - - - - - - Password length - - - 1 - - - 128 - - - 20 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - 8 - - - - - - - Password length - - - 1 - - - 999 - - - 20 - - - - - - - - - - Passphrase - - - - - - - - - - Word Case: - - - - - - - - 0 - 0 - - - - - - - - Word Separator: - - - - - - - - 0 - 0 - - - - Wordlist: - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - QLayout::SetMinimumSize - - - - - 1 - - - 40 - - - 6 - - - 6 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - 8 - - - - - - - 1 - - - 100 - - - 6 - - - - - - - - - Word Count: - - - spinBoxLength - - - - - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Regenerate password - - - - - - Regenerate - - - - - - - Copy password - - - - - - Copy - - - - - - - false - - - Accept password - - - - - - Accept - - - - - - - - - + Qt::Vertical + + QSizePolicy::Expanding + 0 @@ -1219,16 +1137,17 @@ QProgressBar::chunk { editNewPassword - togglePasswordButton + buttonGenerate + buttonCopy tabWidget sliderLength spinBoxLength + buttonAdvancedMode checkBoxUpper checkBoxLower checkBoxNumbers checkBoxSpecialChars checkBoxExtASCII - buttonAdvancedMode checkBoxUpperAdv checkBoxNumbersAdv checkBoxPunctuation @@ -1243,13 +1162,12 @@ QProgressBar::chunk { buttonAddHex checkBoxExcludeAlike checkBoxEnsureEvery - buttonSimpleMode comboBoxWordList sliderWordCount spinBoxWordCount editWordSeparator - buttonGenerate - buttonCopy + wordCaseComboBox + buttonClose buttonApply diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 7b1e85472..948e8456e 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -148,8 +148,6 @@ void EditEntryWidget::setupMain() m_usernameCompleter->setModel(m_usernameCompleterModel); m_mainUi->usernameComboBox->setCompleter(m_usernameCompleter); - m_mainUi->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show")); - m_mainUi->togglePasswordGeneratorButton->setIcon(filePath()->icon("actions", "password-generator")); #ifdef WITH_XC_NETWORKING m_mainUi->fetchFaviconButton->setIcon(filePath()->icon("actions", "favicon-download")); m_mainUi->fetchFaviconButton->setDisabled(true); @@ -157,8 +155,6 @@ void EditEntryWidget::setupMain() m_mainUi->fetchFaviconButton->setVisible(false); #endif - connect(m_mainUi->togglePasswordButton, SIGNAL(toggled(bool)), m_mainUi->passwordEdit, SLOT(setShowPassword(bool))); - connect(m_mainUi->togglePasswordGeneratorButton, SIGNAL(toggled(bool)), SLOT(togglePasswordGeneratorButton(bool))); #ifdef WITH_XC_NETWORKING connect(m_mainUi->fetchFaviconButton, SIGNAL(clicked()), m_iconsWidget, SLOT(downloadFavicon())); connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), m_iconsWidget, SLOT(setUrl(QString))); @@ -166,8 +162,9 @@ void EditEntryWidget::setupMain() #endif connect(m_mainUi->expireCheck, SIGNAL(toggled(bool)), m_mainUi->expireDatePicker, SLOT(setEnabled(bool))); connect(m_mainUi->notesEnabled, SIGNAL(toggled(bool)), this, SLOT(toggleHideNotes(bool))); - m_mainUi->passwordRepeatEdit->enableVerifyMode(m_mainUi->passwordEdit); + connect(m_mainUi->passwordGenerator, SIGNAL(appliedPassword(QString)), SLOT(setGeneratedPassword(QString))); + connect(m_mainUi->passwordGenerator, SIGNAL(closePasswordGenerator()), SLOT(togglePasswordGenerator())); m_mainUi->expirePresets->setMenu(createPresetsMenu()); connect(m_mainUi->expirePresets->menu(), SIGNAL(triggered(QAction*)), this, SLOT(useExpiryPreset(QAction*))); @@ -417,7 +414,6 @@ void EditEntryWidget::setupEntryUpdate() connect(m_mainUi->titleEdit, SIGNAL(textChanged(QString)), this, SLOT(setModified())); connect(m_mainUi->usernameComboBox->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(setModified())); connect(m_mainUi->passwordEdit, SIGNAL(textChanged(QString)), this, SLOT(setModified())); - connect(m_mainUi->passwordRepeatEdit, SIGNAL(textChanged(QString)), this, SLOT(setModified())); connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(setModified())); #ifdef WITH_XC_NETWORKING connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(updateFaviconButtonEnable(QString))); @@ -809,7 +805,6 @@ void EditEntryWidget::setForms(Entry* entry, bool restore) m_mainUi->usernameComboBox->lineEdit()->setReadOnly(m_history); m_mainUi->urlEdit->setReadOnly(m_history); m_mainUi->passwordEdit->setReadOnly(m_history); - m_mainUi->passwordRepeatEdit->setReadOnly(m_history); m_mainUi->expireCheck->setEnabled(!m_history); m_mainUi->expireDatePicker->setReadOnly(m_history); m_mainUi->notesEnabled->setChecked(!config()->get("security/hidenotes").toBool()); @@ -821,8 +816,7 @@ void EditEntryWidget::setForms(Entry* entry, bool restore) } else { m_mainUi->notesEdit->setFont(Font::defaultFont()); } - m_mainUi->togglePasswordGeneratorButton->setChecked(false); - m_mainUi->togglePasswordGeneratorButton->setDisabled(m_history); + m_mainUi->passwordGenerator->setVisible(false); m_mainUi->passwordGenerator->reset(entry->password().length()); m_advancedUi->attachmentsWidget->setReadOnly(m_history); @@ -849,11 +843,14 @@ void EditEntryWidget::setForms(Entry* entry, bool restore) m_mainUi->usernameComboBox->lineEdit()->setText(entry->username()); m_mainUi->urlEdit->setText(entry->url()); m_mainUi->passwordEdit->setText(entry->password()); - m_mainUi->passwordRepeatEdit->setText(entry->password()); + m_mainUi->passwordEdit->setShowPassword(config()->get("security/passwordscleartext").toBool()); + if (!m_history) { + m_mainUi->passwordEdit->enablePasswordGenerator(true); + } m_mainUi->expireCheck->setChecked(entry->timeInfo().expires()); m_mainUi->expireDatePicker->setDateTime(entry->timeInfo().expiryTime().toLocalTime()); m_mainUi->expirePresets->setEnabled(!m_history); - m_mainUi->togglePasswordButton->setChecked(config()->get("security/passwordscleartext").toBool()); + QList commonUsernames = m_db->commonUsernames(); m_usernameCompleterModel->setStringList(commonUsernames); @@ -973,13 +970,8 @@ bool EditEntryWidget::commitEntry() return true; } - if (!passwordsEqual()) { - showMessage(tr("Different passwords supplied."), MessageWidget::Error); - return false; - } - // Ask the user to apply the generator password, if open - if (m_mainUi->togglePasswordGeneratorButton->isChecked() + if (m_mainUi->passwordGenerator->isVisible() && m_mainUi->passwordGenerator->getGeneratedPassword() != m_mainUi->passwordEdit->text()) { auto answer = MessageBox::question(this, tr("Apply generated password?"), @@ -992,7 +984,7 @@ bool EditEntryWidget::commitEntry() } // Hide the password generator - m_mainUi->togglePasswordGeneratorButton->setChecked(false); + m_mainUi->passwordGenerator->setVisible(false); if (m_advancedUi->attributesView->currentIndex().isValid() && m_advancedUi->attributesEdit->isEnabled()) { QString key = m_attributesModel->keyByIndex(m_advancedUi->attributesView->currentIndex()); @@ -1139,7 +1131,6 @@ void EditEntryWidget::clear() m_mainUi->titleEdit->setText(""); m_mainUi->passwordEdit->setText(""); - m_mainUi->passwordRepeatEdit->setText(""); m_mainUi->urlEdit->setText(""); m_mainUi->notesEdit->clear(); @@ -1151,25 +1142,20 @@ void EditEntryWidget::clear() hideMessage(); } -void EditEntryWidget::togglePasswordGeneratorButton(bool checked) +void EditEntryWidget::togglePasswordGenerator() { - if (checked) { + bool visible = m_mainUi->passwordGenerator->isVisible(); + if (!visible) { m_mainUi->passwordGenerator->regeneratePassword(); + m_mainUi->passwordGenerator->setPasswordVisible(m_mainUi->passwordEdit->isPasswordVisible()); } - m_mainUi->passwordGenerator->setVisible(checked); -} - -bool EditEntryWidget::passwordsEqual() -{ - return m_mainUi->passwordEdit->text() == m_mainUi->passwordRepeatEdit->text(); + m_mainUi->passwordGenerator->setVisible(!visible); } void EditEntryWidget::setGeneratedPassword(const QString& password) { m_mainUi->passwordEdit->setText(password); - m_mainUi->passwordRepeatEdit->setText(password); - - m_mainUi->togglePasswordGeneratorButton->setChecked(false); + m_mainUi->passwordGenerator->setVisible(false); } #ifdef WITH_XC_NETWORKING diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index dafc4a283..be994b88c 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -83,7 +83,7 @@ private slots: void acceptEntry(); bool commitEntry(); void cancel(); - void togglePasswordGeneratorButton(bool checked); + void togglePasswordGenerator(); void setGeneratedPassword(const QString& password); #ifdef WITH_XC_NETWORKING void updateFaviconButtonEnable(const QString& url); diff --git a/src/gui/entry/EditEntryWidgetMain.ui b/src/gui/entry/EditEntryWidgetMain.ui index 54140fcd9..2be52a28e 100644 --- a/src/gui/entry/EditEntryWidgetMain.ui +++ b/src/gui/entry/EditEntryWidgetMain.ui @@ -20,122 +20,16 @@ 0 - - - - URL: - - - - - - - - - Url field - - - - - - - Download favicon for URL - - - Download favicon for URL - - - - - - - - - - - - Password: - - - - - - - - - Repeat password field - - - QLineEdit::Password - - - - - - - Toggle password generator - - - Toggle password generator - - - true - - - - - - - - - - - Password field - - - QLineEdit::Password - - - - - - - Toggle password visibility - - - Toggle password visibility - - - true - - - - - - - - - Repeat: - - - - - - - Title: - - - - - + + - Toggle notes visible + Toggle expiration - Toggle notes visible + Toggle expiration - Notes + Expires @@ -194,10 +88,48 @@ + + + + Username field + + + + + + + + + + Title field + + + + + + + + + Url field + + + + + + + Download favicon for URL + + + Download favicon for URL + + + + + - false + true Toggle the checkbox to reveal the notes section. @@ -207,6 +139,13 @@ + + + + URL: + + + @@ -214,34 +153,50 @@ - - - - Title field - - - - - - - Username field - - - - - + + - Toggle expiration + Toggle notes visible - Toggle expiration + Toggle notes visible - Expires + Notes + + + + + + + Password: + + + + + + + Password field + + + QLineEdit::Password + + + + 35 + 0 + 23 + 19 + + + + Title: + + @@ -267,9 +222,6 @@ titleEdit usernameComboBox passwordEdit - togglePasswordButton - passwordRepeatEdit - togglePasswordGeneratorButton urlEdit fetchFaviconButton expireCheck diff --git a/src/gui/masterkey/PasswordEditWidget.cpp b/src/gui/masterkey/PasswordEditWidget.cpp index 96b5fd305..60689e920 100644 --- a/src/gui/masterkey/PasswordEditWidget.cpp +++ b/src/gui/masterkey/PasswordEditWidget.cpp @@ -53,7 +53,7 @@ bool PasswordEditWidget::addToCompositeKey(QSharedPointer key) */ void PasswordEditWidget::setPasswordVisible(bool visible) { - m_compUi->togglePasswordButton->setChecked(visible); + m_compUi->enterPasswordEdit->setShowPassword(visible); } /** @@ -61,7 +61,7 @@ void PasswordEditWidget::setPasswordVisible(bool visible) */ bool PasswordEditWidget::isPasswordVisible() const { - return m_compUi->togglePasswordButton->isChecked(); + return m_compUi->enterPasswordEdit->isPasswordVisible(); } bool PasswordEditWidget::isEmpty() const @@ -73,16 +73,8 @@ QWidget* PasswordEditWidget::componentEditWidget() { m_compEditWidget = new QWidget(); m_compUi->setupUi(m_compEditWidget); - m_compUi->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show")); - m_compUi->passwordGeneratorButton->setIcon(filePath()->icon("actions", "password-generator")); - m_compUi->repeatPasswordEdit->enableVerifyMode(m_compUi->enterPasswordEdit); - - connect(m_compUi->togglePasswordButton, - SIGNAL(toggled(bool)), - m_compUi->enterPasswordEdit, - SLOT(setShowPassword(bool))); - connect(m_compUi->passwordGeneratorButton, SIGNAL(clicked(bool)), SLOT(showPasswordGenerator())); - + m_compUi->enterPasswordEdit->enablePasswordGenerator(); + m_compUi->enterPasswordEdit->setRepeatPartner(m_compUi->repeatPasswordEdit); return m_compEditWidget; } @@ -113,26 +105,6 @@ bool PasswordEditWidget::validate(QString& errorMessage) const return true; } -void PasswordEditWidget::showPasswordGenerator() -{ - QDialog pwDialog; - pwDialog.setWindowTitle(tr("Generate master password")); - - auto layout = new QVBoxLayout(); - pwDialog.setLayout(layout); - - auto pwGenerator = new PasswordGeneratorWidget(&pwDialog); - layout->addWidget(pwGenerator); - - pwGenerator->setStandaloneMode(false); - connect(pwGenerator, SIGNAL(appliedPassword(QString)), SLOT(setPassword(QString))); - connect(pwGenerator, SIGNAL(dialogTerminated()), &pwDialog, SLOT(close())); - - pwGenerator->setPasswordVisible(isPasswordVisible()); - - pwDialog.exec(); -} - void PasswordEditWidget::setPassword(const QString& password) { Q_ASSERT(m_compEditWidget); diff --git a/src/gui/masterkey/PasswordEditWidget.h b/src/gui/masterkey/PasswordEditWidget.h index 57c225c1f..802219451 100644 --- a/src/gui/masterkey/PasswordEditWidget.h +++ b/src/gui/masterkey/PasswordEditWidget.h @@ -47,7 +47,6 @@ protected: void hideEvent(QHideEvent* event) override; private slots: - void showPasswordGenerator(); void setPassword(const QString& password); private: diff --git a/src/gui/masterkey/PasswordEditWidget.ui b/src/gui/masterkey/PasswordEditWidget.ui index d0a85eb59..d8382ed94 100644 --- a/src/gui/masterkey/PasswordEditWidget.ui +++ b/src/gui/masterkey/PasswordEditWidget.ui @@ -31,49 +31,26 @@ - - - - - Password field - - - QLineEdit::Password - - - - - - - Toggle password visibility - - - true - - - - - - - - - - - Repeat password field - - - QLineEdit::Password - - - - - - - Toggle password generator - - - - + + + + 0 + 0 + + + + + 300 + 0 + + + + Password field + + + QLineEdit::Password + + @@ -82,6 +59,28 @@ + + + + + 0 + 0 + + + + + 300 + 0 + + + + Repeat password field + + + QLineEdit::Password + + + @@ -95,8 +94,6 @@ enterPasswordEdit repeatPasswordEdit - togglePasswordButton - passwordGeneratorButton diff --git a/src/gui/wizard/NewDatabaseWizardPageMasterKey.cpp b/src/gui/wizard/NewDatabaseWizardPageMasterKey.cpp index 130560e27..71bd5e60f 100644 --- a/src/gui/wizard/NewDatabaseWizardPageMasterKey.cpp +++ b/src/gui/wizard/NewDatabaseWizardPageMasterKey.cpp @@ -37,5 +37,5 @@ NewDatabaseWizardPageMasterKey::~NewDatabaseWizardPageMasterKey() void NewDatabaseWizardPageMasterKey::updateWindowSize() { // ugly workaround for QWizard not managing to react to size changes automatically - QApplication::activeWindow()->adjustSize(); + window()->adjustSize(); } diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 76766c8dc..dd69e3042 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -471,17 +471,6 @@ void TestGui::testEditEntry() QCOMPARE(attrTextEdit->toPlainText(), attrText); editEntryWidget->setCurrentPage(0); - // Test mismatch passwords - auto* passwordEdit = editEntryWidget->findChild("passwordEdit"); - QString originalPassword = passwordEdit->text(); - passwordEdit->setText("newpass"); - QTest::mouseClick(okButton, Qt::LeftButton); - auto* messageWiget = editEntryWidget->findChild("messageWidget"); - QTRY_VERIFY(messageWiget->isVisible()); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode); - QCOMPARE(passwordEdit->text(), QString("newpass")); - passwordEdit->setText(originalPassword); - // Save the edit (press OK) QTest::mouseClick(okButton, Qt::LeftButton); QApplication::processEvents(); @@ -506,7 +495,6 @@ void TestGui::testEditEntry() titleEdit->setText("multiline\ntitle"); editEntryWidget->findChild("usernameComboBox")->lineEdit()->setText("multiline\nusername"); editEntryWidget->findChild("passwordEdit")->setText("multiline\npassword"); - editEntryWidget->findChild("passwordRepeatEdit")->setText("multiline\npassword"); editEntryWidget->findChild("urlEdit")->setText("multiline\nurl"); QTest::mouseClick(okButton, Qt::LeftButton); @@ -615,9 +603,7 @@ void TestGui::testAddEntry() QTest::keyClicks(usernameComboBox, "Auto"); QTest::keyPress(usernameComboBox, Qt::Key_Right); auto* passwordEdit = editEntryWidget->findChild("passwordEdit"); - auto* passwordRepeatEdit = editEntryWidget->findChild("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); @@ -663,8 +649,10 @@ void TestGui::testPasswordEntryEntropy() QTest::keyClicks(titleEdit, "test"); // Open the password generator - auto* generatorButton = editEntryWidget->findChild("togglePasswordGeneratorButton"); - QTest::mouseClick(generatorButton, Qt::LeftButton); + auto* passwordEdit = editEntryWidget->findChild(); + QVERIFY(passwordEdit); + QTest::mouseClick(passwordEdit, Qt::LeftButton); + QTest::keyClick(passwordEdit, Qt::Key_G, Qt::ControlModifier); // Type in some password auto* editNewPassword = editEntryWidget->findChild("editNewPassword"); @@ -735,8 +723,10 @@ void TestGui::testDicewareEntryEntropy() QTest::keyClicks(titleEdit, "test"); // Open the password generator - auto* generatorButton = editEntryWidget->findChild("togglePasswordGeneratorButton"); - QTest::mouseClick(generatorButton, Qt::LeftButton); + auto* passwordEdit = editEntryWidget->findChild(); + QVERIFY(passwordEdit); + QTest::mouseClick(passwordEdit, Qt::LeftButton); + QTest::keyClick(passwordEdit, Qt::Key_G, Qt::ControlModifier); // Select Diceware auto* tabWidget = editEntryWidget->findChild("tabWidget"); @@ -1440,7 +1430,6 @@ int TestGui::addCannedEntries() auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); auto* titleEdit = editEntryWidget->findChild("titleEdit"); auto* passwordEdit = editEntryWidget->findChild("passwordEdit"); - auto* passwordRepeatEdit = editEntryWidget->findChild("passwordRepeatEdit"); // Add entry "test" and confirm added QTest::mouseClick(entryNewWidget, Qt::LeftButton); @@ -1453,7 +1442,6 @@ int TestGui::addCannedEntries() QTest::mouseClick(entryNewWidget, Qt::LeftButton); QTest::keyClicks(titleEdit, "something 2"); QTest::keyClicks(passwordEdit, "something 2"); - QTest::keyClicks(passwordRepeatEdit, "something 2"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); ++entries_added;