Better handling of "Lock on Minimize" setting

* Fix #1090 - delay locking databases after minimize to allow for clipboard use, Auto-Type use, and browser integration use.

* Fix #6757 - prevent setting both minimize on unlock and lock on minimize settings at the same time.
This commit is contained in:
Jonathan White 2022-06-27 23:21:40 -04:00
parent dab6d9408e
commit a6d3f973fa
7 changed files with 57 additions and 12 deletions

View File

@ -227,6 +227,10 @@
<source>Select backup storage directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting cannot be enabled when minimize on unlock is enabled.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ApplicationSettingsWidgetGeneral</name>

View File

@ -134,6 +134,15 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
m_secUi->lockDatabaseIdleSpinBox, SLOT(setEnabled(bool)));
// clang-format on
connect(m_generalUi->minimizeAfterUnlockCheckBox, &QCheckBox::toggled, this, [this](bool state) {
if (state) {
m_secUi->lockDatabaseMinimizeCheckBox->setChecked(false);
}
m_secUi->lockDatabaseMinimizeCheckBox->setToolTip(
state ? tr("This setting cannot be enabled when minimize on unlock is enabled.") : "");
m_secUi->lockDatabaseMinimizeCheckBox->setEnabled(!state);
});
// Disable mouse wheel grab when scrolling
// This prevents combo box and spinner values from changing without explicit focus
auto mouseWheelFilter = new MouseWheelEventFilter(this);
@ -296,7 +305,8 @@ void ApplicationSettingsWidget::loadSettings()
m_secUi->lockDatabaseIdleCheckBox->setChecked(config()->get(Config::Security_LockDatabaseIdle).toBool());
m_secUi->lockDatabaseIdleSpinBox->setValue(config()->get(Config::Security_LockDatabaseIdleSeconds).toInt());
m_secUi->lockDatabaseMinimizeCheckBox->setChecked(config()->get(Config::Security_LockDatabaseMinimize).toBool());
m_secUi->lockDatabaseMinimizeCheckBox->setChecked(m_secUi->lockDatabaseMinimizeCheckBox->isEnabled()
&& config()->get(Config::Security_LockDatabaseMinimize).toBool());
m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked(
config()->get(Config::Security_LockDatabaseScreenLock).toBool());
m_secUi->fallbackToSearch->setChecked(config()->get(Config::Security_IconDownloadFallback).toBool());

View File

@ -76,14 +76,21 @@ void Clipboard::setText(const QString& text, bool clear)
if (config()->get(Config::Security_ClearClipboard).toBool()) {
int timeout = config()->get(Config::Security_ClearClipboardTimeout).toInt();
if (timeout > 0) {
m_secondsElapsed = -1;
countdownTick();
m_secondsToClear = timeout;
sendCountdownStatus();
m_timer->start(1000);
} else {
clearCopiedText();
}
}
}
}
int Clipboard::secondsToClear()
{
return m_secondsToClear;
}
void Clipboard::clearCopiedText()
{
m_timer->stop();
@ -105,17 +112,20 @@ void Clipboard::clearCopiedText()
void Clipboard::countdownTick()
{
m_secondsElapsed++;
int timeout = config()->get(Config::Security_ClearClipboardTimeout).toInt();
int timeLeft = timeout - m_secondsElapsed;
if (timeLeft <= 0) {
if (--m_secondsToClear <= 0) {
clearCopiedText();
} else {
emit updateCountdown(100 * timeLeft / timeout,
QObject::tr("Clearing the clipboard in %1 second(s)…", "", timeLeft).arg(timeLeft));
sendCountdownStatus();
}
}
void Clipboard::sendCountdownStatus()
{
emit updateCountdown(
100 * m_secondsToClear / config()->get(Config::Security_ClearClipboardTimeout).toInt(),
QObject::tr("Clearing the clipboard in %1 second(s)…", "", m_secondsToClear).arg(m_secondsToClear));
}
Clipboard* Clipboard::instance()
{
if (!m_instance) {

View File

@ -34,6 +34,7 @@ class Clipboard : public QObject
public:
void setText(const QString& text, bool clear = true);
int secondsToClear();
static Clipboard* instance();
@ -51,8 +52,10 @@ private:
static Clipboard* m_instance;
void sendCountdownStatus();
QTimer* m_timer;
int m_secondsElapsed = 0;
int m_secondsToClear = 0;
#ifdef Q_OS_MACOS
// This object lives for the whole program lifetime and we cannot delete it on exit,

View File

@ -63,6 +63,9 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
#ifdef Q_OS_MACOS
connect(macUtils(), SIGNAL(lockDatabases()), SLOT(lockDatabases()));
#endif
m_lockDelayTimer.setSingleShot(true);
connect(&m_lockDelayTimer, &QTimer::timeout, this, [this] { lockDatabases(); });
}
DatabaseTabWidget::~DatabaseTabWidget()
@ -649,6 +652,18 @@ bool DatabaseTabWidget::lockDatabases()
return numLocked == c;
}
void DatabaseTabWidget::lockDatabasesDelayed()
{
// Delay at least 1 second and up to 20 seconds depending on clipboard state.
// This allows for Auto-Type, Browser Extension, and clipboard to function
// even with "Lock on Minimize" setting enabled.
int lockDelay = qBound(1, clipboard()->secondsToClear(), 20);
m_lockDelayTimer.setInterval(lockDelay * 1000);
if (!m_lockDelayTimer.isActive()) {
m_lockDelayTimer.start();
}
}
/**
* Unlock a database with an unlock popup dialog.
*

View File

@ -22,6 +22,7 @@
#include "gui/MessageWidget.h"
#include <QTabWidget>
#include <QTimer>
class Database;
class DatabaseWidget;
@ -72,6 +73,7 @@ public slots:
void exportToHtml();
bool lockDatabases();
void lockDatabasesDelayed();
void closeDatabaseFromSender();
void unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent);
void unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent, const QString& filePath);
@ -112,6 +114,7 @@ private:
QPointer<DatabaseWidgetStateSync> m_dbWidgetStateSync;
QPointer<DatabaseWidget> m_dbWidgetPendingLock;
QPointer<DatabaseOpenDialog> m_databaseOpenDialog;
QTimer m_lockDelayTimer;
};
#endif // KEEPASSX_DATABASETABWIDGET_H

View File

@ -1326,7 +1326,7 @@ void MainWindow::changeEvent(QEvent* event)
}
if (config()->get(Config::Security_LockDatabaseMinimize).toBool()) {
m_ui->tabWidget->lockDatabases();
m_ui->tabWidget->lockDatabasesDelayed();
}
} else {
QMainWindow::changeEvent(event);
@ -1674,7 +1674,7 @@ void MainWindow::hideWindow()
}
if (config()->get(Config::Security_LockDatabaseMinimize).toBool()) {
m_ui->tabWidget->lockDatabases();
m_ui->tabWidget->lockDatabasesDelayed();
}
}