diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0ceccdd9a..a441b475b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -173,6 +173,7 @@ set(keepassx_SOURCES
gui/osutils/OSUtilsBase.cpp
gui/settings/SettingsWidget.cpp
gui/widgets/ElidedLabel.cpp
+ gui/widgets/KPToolBar.cpp
gui/widgets/PopupHelpWidget.cpp
gui/wizard/NewDatabaseWizard.cpp
gui/wizard/NewDatabaseWizardPage.cpp
diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp
index bb0a6ec41..a91d15b3b 100644
--- a/src/gui/DatabaseWidget.cpp
+++ b/src/gui/DatabaseWidget.cpp
@@ -1391,9 +1391,8 @@ void DatabaseWidget::onGroupChanged()
// Intercept group changes if in search mode
if (isSearchActive() && m_searchLimitGroup) {
search(m_lastSearchText);
- } else if (isSearchActive()) {
- endSearch();
} else {
+ endSearch();
m_entryView->displayGroup(group);
}
diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp
index 7a53f144d..659ac8457 100644
--- a/src/gui/MainWindow.cpp
+++ b/src/gui/MainWindow.cpp
@@ -118,6 +118,17 @@ MainWindow::MainWindow()
m_searchWidgetAction = m_ui->toolBar->addWidget(m_searchWidget);
m_searchWidgetAction->setEnabled(false);
+ new QShortcut(QKeySequence::Find, this, SLOT(focusSearchWidget()));
+
+ connect(m_searchWidget, &SearchWidget::searchCanceled, this, [this] {
+ m_ui->toolBar->setExpanded(false);
+ m_ui->toolBar->setVisible(!config()->get(Config::GUI_HideToolbar).toBool());
+ });
+ connect(m_searchWidget, &SearchWidget::lostFocus, this, [this] {
+ m_ui->toolBar->setExpanded(false);
+ m_ui->toolBar->setVisible(!config()->get(Config::GUI_HideToolbar).toBool());
+ });
+
m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size();
m_entryContextMenu = new QMenu(this);
@@ -1227,7 +1238,10 @@ void MainWindow::keyPressEvent(QKeyEvent* event)
dbWidget->focusOnEntries(true);
return;
} else if (event->key() == Qt::Key_F3) {
- m_searchWidget->searchFocus();
+ focusSearchWidget();
+ return;
+ } else if (event->key() == Qt::Key_Escape && dbWidget->isSearchActive()) {
+ m_searchWidget->clearSearch();
return;
}
}
@@ -1248,13 +1262,13 @@ bool MainWindow::focusNextPrevChild(bool next)
} else if (m_ui->tabWidget->hasFocus()) {
dbWidget->setFocus(Qt::TabFocusReason);
} else {
- m_searchWidget->setFocus(Qt::TabFocusReason);
+ focusSearchWidget();
}
} else {
if (m_searchWidget->hasFocus()) {
dbWidget->setFocus(Qt::BacktabFocusReason);
} else if (m_ui->tabWidget->hasFocus()) {
- m_searchWidget->setFocus(Qt::BacktabFocusReason);
+ focusSearchWidget();
} else {
m_ui->tabWidget->setFocus(Qt::BacktabFocusReason);
}
@@ -1266,6 +1280,15 @@ bool MainWindow::focusNextPrevChild(bool next)
return QMainWindow::focusNextPrevChild(next);
}
+void MainWindow::focusSearchWidget()
+{
+ if (m_searchWidgetAction->isEnabled()) {
+ m_ui->toolBar->setVisible(true);
+ m_ui->toolBar->setExpanded(true);
+ m_searchWidget->focusSearch();
+ }
+}
+
void MainWindow::saveWindowInformation()
{
if (isVisible()) {
diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h
index 8a99d4432..a80346dfe 100644
--- a/src/gui/MainWindow.h
+++ b/src/gui/MainWindow.h
@@ -143,6 +143,7 @@ private slots:
private slots:
void updateTrayIcon();
void updateProgressBar(int percentage, QString message);
+ void focusSearchWidget();
private:
static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0);
diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui
index 62d6cdd93..679578f1b 100644
--- a/src/gui/MainWindow.ui
+++ b/src/gui/MainWindow.ui
@@ -394,7 +394,7 @@
-
+
Qt::PreventContextMenu
@@ -1043,6 +1043,11 @@
1
+
+ KPToolBar
+ QToolBar
+
+
diff --git a/src/gui/SearchWidget.cpp b/src/gui/SearchWidget.cpp
index 6ade47e2e..4cf53a9ac 100644
--- a/src/gui/SearchWidget.cpp
+++ b/src/gui/SearchWidget.cpp
@@ -48,8 +48,8 @@ SearchWidget::SearchWidget(QWidget* parent)
connect(m_ui->helpIcon, SIGNAL(triggered()), SLOT(toggleHelp()));
connect(m_ui->searchIcon, SIGNAL(triggered()), SLOT(showSearchMenu()));
connect(m_searchTimer, SIGNAL(timeout()), SLOT(startSearch()));
- connect(m_clearSearchTimer, SIGNAL(timeout()), m_ui->searchEdit, SLOT(clear()));
- connect(this, SIGNAL(escapePressed()), m_ui->searchEdit, SLOT(clear()));
+ connect(m_clearSearchTimer, SIGNAL(timeout()), SLOT(clearSearch()));
+ connect(this, SIGNAL(escapePressed()), SLOT(clearSearch()));
new QShortcut(QKeySequence::Find, this, SLOT(searchFocus()), nullptr, Qt::ApplicationShortcut);
new QShortcut(Qt::Key_Escape, m_ui->searchEdit, SLOT(clear()), nullptr, Qt::ApplicationShortcut);
@@ -109,7 +109,7 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* event)
return true;
}
}
- } else if (event->type() == QEvent::FocusOut && !m_ui->searchEdit->text().isEmpty()) {
+ } else if (event->type() == QEvent::FocusOut) {
if (config()->get(Config::Security_ClearSearch).toBool()) {
int timeout = config()->get(Config::Security_ClearSearchTimeout).toInt();
if (timeout > 0) {
@@ -117,6 +117,7 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* event)
m_clearSearchTimer->start(timeout * 60000); // 60 sec * 1000 ms
}
}
+ emit lostFocus();
} else if (event->type() == QEvent::FocusIn) {
// Never clear the search if we are using it
m_clearSearchTimer->stop();
@@ -133,10 +134,10 @@ void SearchWidget::connectSignals(SignalMultiplexer& mx)
mx.connect(this, SIGNAL(limitGroupChanged(bool)), SLOT(setSearchLimitGroup(bool)));
mx.connect(this, SIGNAL(copyPressed()), SLOT(copyPassword()));
mx.connect(this, SIGNAL(downPressed()), SLOT(focusOnEntries()));
- mx.connect(SIGNAL(clearSearch()), m_ui->searchEdit, SLOT(clear()));
+ mx.connect(SIGNAL(clearSearch()), this, SLOT(clearSearch()));
mx.connect(SIGNAL(entrySelectionChanged()), this, SLOT(resetSearchClearTimer()));
mx.connect(SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(resetSearchClearTimer()));
- mx.connect(SIGNAL(databaseUnlocked()), this, SLOT(searchFocus()));
+ mx.connect(SIGNAL(databaseUnlocked()), this, SLOT(focusSearch()));
mx.connect(m_ui->searchEdit, SIGNAL(returnPressed()), SLOT(switchToEntryEdit()));
}
@@ -149,7 +150,7 @@ void SearchWidget::databaseChanged(DatabaseWidget* dbWidget)
emit caseSensitiveChanged(m_actionCaseSensitive->isChecked());
emit limitGroupChanged(m_actionLimitGroup->isChecked());
} else {
- m_ui->searchEdit->clear();
+ clearSearch();
}
}
@@ -201,12 +202,18 @@ void SearchWidget::setLimitGroup(bool state)
updateLimitGroup();
}
-void SearchWidget::searchFocus()
+void SearchWidget::focusSearch()
{
m_ui->searchEdit->setFocus();
m_ui->searchEdit->selectAll();
}
+void SearchWidget::clearSearch()
+{
+ m_ui->searchEdit->clear();
+ emit searchCanceled();
+}
+
void SearchWidget::toggleHelp()
{
if (m_helpWidget->isVisible()) {
diff --git a/src/gui/SearchWidget.h b/src/gui/SearchWidget.h
index eefdff6ee..b2192f54d 100644
--- a/src/gui/SearchWidget.h
+++ b/src/gui/SearchWidget.h
@@ -52,16 +52,19 @@ protected:
signals:
void search(const QString& text);
+ void searchCanceled();
void caseSensitiveChanged(bool state);
void limitGroupChanged(bool state);
void escapePressed();
void copyPressed();
void downPressed();
void enterPressed();
+ void lostFocus();
public slots:
void databaseChanged(DatabaseWidget* dbWidget = nullptr);
- void searchFocus();
+ void focusSearch();
+ void clearSearch();
private slots:
void startSearchTimer();
diff --git a/src/gui/widgets/KPToolBar.cpp b/src/gui/widgets/KPToolBar.cpp
new file mode 100644
index 000000000..88b75b9b1
--- /dev/null
+++ b/src/gui/widgets/KPToolBar.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "KPToolBar.h"
+
+#include
+#include
+#include
+
+KPToolBar::KPToolBar(const QString& title, QWidget* parent)
+ : QToolBar(title, parent)
+{
+ init();
+}
+
+KPToolBar::KPToolBar(QWidget* parent)
+ : QToolBar(parent)
+{
+ init();
+}
+
+void KPToolBar::init()
+{
+ m_expandButton = findChild("qt_toolbar_ext_button");
+ m_expandTimer.setSingleShot(true);
+ connect(&m_expandTimer, &QTimer::timeout, this, [this] { setExpanded(false); });
+}
+
+bool KPToolBar::isExpanded()
+{
+ return !canExpand() || (canExpand() && m_expandButton->isChecked());
+}
+
+bool KPToolBar::canExpand()
+{
+ return m_expandButton && m_expandButton->isVisible();
+}
+
+void KPToolBar::setExpanded(bool state)
+{
+ if (canExpand() && !QMetaObject::invokeMethod(layout(), "setExpanded", Q_ARG(bool, state))) {
+ qWarning("Toolbar: Cannot invoke setExpanded!");
+ }
+}
+
+bool KPToolBar::event(QEvent* event)
+{
+ // Override events handled by the base class for better UX when using an expandable toolbar.
+ switch (event->type()) {
+ case QEvent::Leave:
+ // Hide the toolbar after 2 seconds of mouse exit
+ m_expandTimer.start(2000);
+ return true;
+ case QEvent::Enter:
+ // Mouse came back in, stop hiding timer
+ m_expandTimer.stop();
+ return true;
+ default:
+ return QToolBar::event(event);
+ }
+}
diff --git a/src/gui/widgets/KPToolBar.h b/src/gui/widgets/KPToolBar.h
new file mode 100644
index 000000000..8883982bd
--- /dev/null
+++ b/src/gui/widgets/KPToolBar.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef KEEPASSXC_KPTOOLBAR_H
+#define KEEPASSXC_KPTOOLBAR_H
+
+#include
+#include
+#include
+
+class QAbstractButton;
+
+class KPToolBar : public QToolBar
+{
+ Q_OBJECT
+
+public:
+ explicit KPToolBar(const QString& title, QWidget* parent = nullptr);
+ explicit KPToolBar(QWidget* parent = nullptr);
+ ~KPToolBar() override = default;
+
+ bool isExpanded();
+ bool canExpand();
+
+public slots:
+ void setExpanded(bool state);
+
+protected:
+ bool event(QEvent* event) override;
+
+private:
+ void init();
+
+ QTimer m_expandTimer;
+ QPointer m_expandButton;
+};
+
+#endif // KEEPASSXC_KPTOOLBAR_H