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> <source>Select backup storage directory</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>This setting cannot be enabled when minimize on unlock is enabled.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ApplicationSettingsWidgetGeneral</name> <name>ApplicationSettingsWidgetGeneral</name>

View File

@ -134,6 +134,15 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
m_secUi->lockDatabaseIdleSpinBox, SLOT(setEnabled(bool))); m_secUi->lockDatabaseIdleSpinBox, SLOT(setEnabled(bool)));
// clang-format on // 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 // Disable mouse wheel grab when scrolling
// This prevents combo box and spinner values from changing without explicit focus // This prevents combo box and spinner values from changing without explicit focus
auto mouseWheelFilter = new MouseWheelEventFilter(this); auto mouseWheelFilter = new MouseWheelEventFilter(this);
@ -296,7 +305,8 @@ void ApplicationSettingsWidget::loadSettings()
m_secUi->lockDatabaseIdleCheckBox->setChecked(config()->get(Config::Security_LockDatabaseIdle).toBool()); m_secUi->lockDatabaseIdleCheckBox->setChecked(config()->get(Config::Security_LockDatabaseIdle).toBool());
m_secUi->lockDatabaseIdleSpinBox->setValue(config()->get(Config::Security_LockDatabaseIdleSeconds).toInt()); 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( m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked(
config()->get(Config::Security_LockDatabaseScreenLock).toBool()); config()->get(Config::Security_LockDatabaseScreenLock).toBool());
m_secUi->fallbackToSearch->setChecked(config()->get(Config::Security_IconDownloadFallback).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()) { if (config()->get(Config::Security_ClearClipboard).toBool()) {
int timeout = config()->get(Config::Security_ClearClipboardTimeout).toInt(); int timeout = config()->get(Config::Security_ClearClipboardTimeout).toInt();
if (timeout > 0) { if (timeout > 0) {
m_secondsElapsed = -1; m_secondsToClear = timeout;
countdownTick(); sendCountdownStatus();
m_timer->start(1000); m_timer->start(1000);
} else {
clearCopiedText();
} }
} }
} }
} }
int Clipboard::secondsToClear()
{
return m_secondsToClear;
}
void Clipboard::clearCopiedText() void Clipboard::clearCopiedText()
{ {
m_timer->stop(); m_timer->stop();
@ -105,17 +112,20 @@ void Clipboard::clearCopiedText()
void Clipboard::countdownTick() void Clipboard::countdownTick()
{ {
m_secondsElapsed++; if (--m_secondsToClear <= 0) {
int timeout = config()->get(Config::Security_ClearClipboardTimeout).toInt();
int timeLeft = timeout - m_secondsElapsed;
if (timeLeft <= 0) {
clearCopiedText(); clearCopiedText();
} else { } else {
emit updateCountdown(100 * timeLeft / timeout, sendCountdownStatus();
QObject::tr("Clearing the clipboard in %1 second(s)…", "", timeLeft).arg(timeLeft));
} }
} }
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() Clipboard* Clipboard::instance()
{ {
if (!m_instance) { if (!m_instance) {

View File

@ -34,6 +34,7 @@ class Clipboard : public QObject
public: public:
void setText(const QString& text, bool clear = true); void setText(const QString& text, bool clear = true);
int secondsToClear();
static Clipboard* instance(); static Clipboard* instance();
@ -51,8 +52,10 @@ private:
static Clipboard* m_instance; static Clipboard* m_instance;
void sendCountdownStatus();
QTimer* m_timer; QTimer* m_timer;
int m_secondsElapsed = 0; int m_secondsToClear = 0;
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
// This object lives for the whole program lifetime and we cannot delete it on exit, // 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 #ifdef Q_OS_MACOS
connect(macUtils(), SIGNAL(lockDatabases()), SLOT(lockDatabases())); connect(macUtils(), SIGNAL(lockDatabases()), SLOT(lockDatabases()));
#endif #endif
m_lockDelayTimer.setSingleShot(true);
connect(&m_lockDelayTimer, &QTimer::timeout, this, [this] { lockDatabases(); });
} }
DatabaseTabWidget::~DatabaseTabWidget() DatabaseTabWidget::~DatabaseTabWidget()
@ -649,6 +652,18 @@ bool DatabaseTabWidget::lockDatabases()
return numLocked == c; 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. * Unlock a database with an unlock popup dialog.
* *

View File

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

View File

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