Skip to content

Commit

Permalink
Fix changing focus around the main window using tab
Browse files Browse the repository at this point in the history
* Override Qt's default [broken] behavior of handling Tab/Shift+Tab to navigate around the MainWindow. Completely fixes trapped focus.

* Improve handling of search results when navigating the UI.

* Fix selecting first entry after ending a search.

* Add keyboard shortcuts to directly focus on search (F1), Group List (F2), and Entry List (F3)

* Fixes #2878, #4636, and #4221
  • Loading branch information
droidmonkey committed Apr 22, 2020
1 parent 7f98323 commit 9e4aec1
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 62 deletions.
73 changes: 61 additions & 12 deletions src/gui/DatabaseWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,9 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
connect(m_previewView, SIGNAL(errorOccurred(QString)), SLOT(showErrorMessage(QString)));
connect(m_previewView, SIGNAL(entryUrlActivated(Entry*)), SLOT(openUrlForEntry(Entry*)));
connect(m_entryView, SIGNAL(viewStateChanged()), SIGNAL(entryViewStateChanged()));
connect(m_groupView, SIGNAL(groupSelectionChanged(Group*)), SLOT(onGroupChanged(Group*)));
connect(m_groupView, SIGNAL(groupSelectionChanged(Group*)), SIGNAL(groupChanged()));
connect(m_groupView, SIGNAL(groupSelectionChanged()), SLOT(onGroupChanged()));
connect(m_groupView, SIGNAL(groupSelectionChanged()), SIGNAL(groupChanged()));
connect(m_groupView, &GroupView::groupFocused, this, [this] { m_previewView->setGroup(currentGroup()); });
connect(m_entryView, SIGNAL(entryActivated(Entry*,EntryModel::ModelColumn)),
SLOT(entryActivationSignalReceived(Entry*,EntryModel::ModelColumn)));
connect(m_entryView, SIGNAL(entrySelectionChanged(Entry*)), SLOT(onEntryChanged(Entry*)));
Expand Down Expand Up @@ -283,6 +284,11 @@ bool DatabaseWidget::isSearchActive() const
return m_entryView->inSearchMode();
}

bool DatabaseWidget::isEntryViewActive() const
{
return currentWidget() == m_mainWidget;
}

bool DatabaseWidget::isEntryEditActive() const
{
return currentWidget() == m_editEntryWidget;
Expand Down Expand Up @@ -616,9 +622,27 @@ bool DatabaseWidget::confirmDeleteEntries(QList<Entry*> entries, bool permanent)
}
}

void DatabaseWidget::setFocus()
void DatabaseWidget::setFocus(Qt::FocusReason reason)
{
if (reason == Qt::BacktabFocusReason) {
m_previewView->setFocus();
} else {
m_groupView->setFocus();
}
}

void DatabaseWidget::focusOnEntries()
{
m_entryView->setFocus();
if (isEntryViewActive()) {
m_entryView->setFocus();
}
}

void DatabaseWidget::focusOnGroups()
{
if (isEntryViewActive()) {
m_groupView->setFocus();
}
}

void DatabaseWidget::copyTitle()
Expand Down Expand Up @@ -925,6 +949,8 @@ int DatabaseWidget::addChildWidget(QWidget* w)

void DatabaseWidget::switchToMainView(bool previousDialogAccepted)
{
setCurrentWidget(m_mainWidget);

if (m_newGroup) {
if (previousDialogAccepted) {
m_newGroup->setParent(m_newParent);
Expand All @@ -950,12 +976,10 @@ void DatabaseWidget::switchToMainView(bool previousDialogAccepted)
m_entryView->setFocus();
}

setCurrentWidget(m_mainWidget);

if (sender() == m_entryView || sender() == m_editEntryWidget) {
onEntryChanged(m_entryView->currentEntry());
} else if (sender() == m_groupView || sender() == m_editGroupWidget) {
onGroupChanged(m_groupView->currentGroup());
onGroupChanged();
}
}

Expand Down Expand Up @@ -1323,8 +1347,10 @@ void DatabaseWidget::setSearchLimitGroup(bool state)
refreshSearch();
}

void DatabaseWidget::onGroupChanged(Group* group)
void DatabaseWidget::onGroupChanged()
{
auto group = m_groupView->currentGroup();

// Intercept group changes if in search mode
if (isSearchActive() && m_searchLimitGroup) {
search(m_lastSearchText);
Expand Down Expand Up @@ -1365,13 +1391,11 @@ QString DatabaseWidget::getCurrentSearch()
void DatabaseWidget::endSearch()
{
if (isSearchActive()) {
emit listModeAboutToActivate();

// Show the normal entry view of the current group
emit listModeAboutToActivate();
m_entryView->displayGroup(currentGroup());
onGroupChanged(currentGroup());

emit listModeActivated();
m_entryView->setFirstEntryActive();
}

m_searchingLabel->setVisible(false);
Expand Down Expand Up @@ -1432,6 +1456,31 @@ void DatabaseWidget::showEvent(QShowEvent* event)
event->accept();
}

bool DatabaseWidget::focusNextPrevChild(bool next)
{
// [parent] <-> GroupView <-> EntryView <-> EntryPreview <-> [parent]
if (next) {
if (m_groupView->hasFocus()) {
m_entryView->setFocus();
return true;
} else if (m_entryView->hasFocus()) {
m_previewView->setFocus();
return true;
}
} else {
if (m_previewView->hasFocus()) {
m_entryView->setFocus();
return true;
} else if (m_entryView->hasFocus()) {
m_groupView->setFocus();
return true;
}
}

// Defer to the parent widget to make a decision
return QStackedWidget::focusNextPrevChild(next);
}

bool DatabaseWidget::lock()
{
if (isLocked()) {
Expand Down
9 changes: 7 additions & 2 deletions src/gui/DatabaseWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,15 @@ class DatabaseWidget : public QStackedWidget
explicit DatabaseWidget(const QString& filePath, QWidget* parent = nullptr);
~DatabaseWidget();

void setFocus(Qt::FocusReason reason);

QSharedPointer<Database> database() const;

DatabaseWidget::Mode currentMode() const;
bool isLocked() const;
bool isSaving() const;
bool isSearchActive() const;
bool isEntryViewActive() const;
bool isEntryEditActive() const;
bool isGroupEditActive() const;

Expand Down Expand Up @@ -161,7 +164,8 @@ public slots:
void cloneEntry();
void deleteSelectedEntries();
void deleteEntries(QList<Entry*> entries);
void setFocus();
void focusOnEntries();
void focusOnGroups();
void copyTitle();
void copyUsername();
void copyPassword();
Expand Down Expand Up @@ -217,6 +221,7 @@ public slots:
protected:
void closeEvent(QCloseEvent* event) override;
void showEvent(QShowEvent* event) override;
bool focusNextPrevChild(bool next) override;

private slots:
void entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column);
Expand All @@ -228,7 +233,7 @@ private slots:
void emitGroupContextMenuRequested(const QPoint& pos);
void emitEntryContextMenuRequested(const QPoint& pos);
void onEntryChanged(Entry* entry);
void onGroupChanged(Group* group);
void onGroupChanged();
void onDatabaseModified();
void connectDatabaseSignals();
void loadDatabase(bool accepted);
Expand Down
2 changes: 2 additions & 0 deletions src/gui/EntryPreviewWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ EntryPreviewWidget::EntryPreviewWidget(QWidget* parent)
connect(m_ui->groupCloseButton, SIGNAL(clicked()), SLOT(hide()));
connect(m_ui->groupTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection);

setFocusProxy(m_ui->entryTabWidget);

#if !defined(WITH_XC_KEESHARE)
removeTab(m_ui->groupTabWidget, m_ui->groupShareTab);
#endif
Expand Down
16 changes: 4 additions & 12 deletions src/gui/EntryPreviewWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>566</width>
<height>169</height>
<height>206</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
Expand Down Expand Up @@ -106,9 +106,6 @@
</item>
<item>
<widget class="QToolButton" name="entryTotpButton">
<property name="focusPolicy">
<enum>Qt::TabFocus</enum>
</property>
<property name="toolTip">
<string>Display current TOTP value</string>
</property>
Expand All @@ -122,9 +119,6 @@
</item>
<item>
<widget class="QToolButton" name="entryCloseButton">
<property name="focusPolicy">
<enum>Qt::TabFocus</enum>
</property>
<property name="toolTip">
<string>Close</string>
</property>
Expand All @@ -137,9 +131,6 @@
</item>
<item>
<widget class="QTabWidget" name="entryTabWidget">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
Expand Down Expand Up @@ -1147,12 +1138,13 @@
</customwidgets>
<tabstops>
<tabstop>entryCloseButton</tabstop>
<tabstop>entryTotpButton</tabstop>
<tabstop>entryTabWidget</tabstop>
<tabstop>togglePasswordButton</tabstop>
<tabstop>toggleEntryNotesButton</tabstop>
<tabstop>entryAutotypeTree</tabstop>
<tabstop>groupCloseButton</tabstop>
<tabstop>groupTabWidget</tabstop>
<tabstop>toggleGroupNotesButton</tabstop>
<tabstop>entryTotpButton</tabstop>
</tabstops>
<resources/>
<connections/>
Expand Down
38 changes: 38 additions & 0 deletions src/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,14 @@ MainWindow::MainWindow()
shortcut = new QShortcut(dbTabModifier + Qt::Key_9, this);
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(m_ui->tabWidget->count() - 1); });

// Allow for direct focus of search, group view, and entry view
shortcut = new QShortcut(Qt::Key_F1, this);
connect(shortcut, SIGNAL(activated()), m_searchWidget, SLOT(setFocus()));
shortcut = new QShortcut(Qt::Key_F2, this);
m_actionMultiplexer.connect(shortcut, SIGNAL(activated()), SLOT(focusOnGroups()));
shortcut = new QShortcut(Qt::Key_F3, this);
m_actionMultiplexer.connect(shortcut, SIGNAL(activated()), SLOT(focusOnEntries()));

// Toggle password and username visibility in entry view
new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C, this, SLOT(togglePasswordsHidden()));
new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_B, this, SLOT(toggleUsernamesHidden()));
Expand Down Expand Up @@ -1123,6 +1131,36 @@ void MainWindow::changeEvent(QEvent* event)
}
}

bool MainWindow::focusNextPrevChild(bool next)
{
// Only navigate around the main window if the database widget is showing the entry view
auto dbWidget = m_ui->tabWidget->currentDatabaseWidget();
if (dbWidget && dbWidget->isVisible() && dbWidget->isEntryViewActive()) {
// Search Widget <-> Tab Widget <-> DbWidget
if (next) {
if (m_searchWidget->hasFocus()) {
m_ui->tabWidget->setFocus(Qt::TabFocusReason);
} else if (m_ui->tabWidget->hasFocus()) {
dbWidget->setFocus(Qt::TabFocusReason);
} else {
m_searchWidget->setFocus(Qt::TabFocusReason);
}
} else {
if (m_searchWidget->hasFocus()) {
dbWidget->setFocus(Qt::BacktabFocusReason);
} else if (m_ui->tabWidget->hasFocus()) {
m_searchWidget->setFocus(Qt::BacktabFocusReason);
} else {
m_ui->tabWidget->setFocus(Qt::BacktabFocusReason);
}
}
return true;
}

// Defer to Qt to make a decision, this maintains normal behavior
return QMainWindow::focusNextPrevChild(next);
}

void MainWindow::saveWindowInformation()
{
if (isVisible()) {
Expand Down
1 change: 1 addition & 0 deletions src/gui/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public slots:
protected:
void closeEvent(QCloseEvent* event) override;
void changeEvent(QEvent* event) override;
bool focusNextPrevChild(bool next) override;

private slots:
void setMenuActionState(DatabaseWidget::Mode mode = DatabaseWidget::Mode::None);
Expand Down
40 changes: 16 additions & 24 deletions src/gui/MainWindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,6 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::TabFocus</enum>
</property>
<property name="currentIndex">
<number>2</number>
</property>
Expand Down Expand Up @@ -129,11 +126,7 @@
<number>0</number>
</property>
<item>
<widget class="ApplicationSettingsWidget" name="settingsWidget" native="true">
<property name="focusPolicy">
<enum>Qt::TabFocus</enum>
</property>
</widget>
<widget class="ApplicationSettingsWidget" name="settingsWidget" native="true"/>
</item>
</layout>
</widget>
Expand All @@ -158,11 +151,7 @@
</spacer>
</item>
<item>
<widget class="WelcomeWidget" name="welcomeWidget" native="true">
<property name="focusPolicy">
<enum>Qt::TabFocus</enum>
</property>
</widget>
<widget class="WelcomeWidget" name="welcomeWidget" native="true"/>
</item>
<item>
<spacer name="horizontalSpacer_2">
Expand Down Expand Up @@ -196,11 +185,20 @@
<number>60</number>
</property>
<item>
<widget class="PasswordGeneratorWidget" name="passwordGeneratorWidget" native="true">
<property name="focusPolicy">
<enum>Qt::TabFocus</enum>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="PasswordGeneratorWidget" name="passwordGeneratorWidget" native="true"/>
</item>
<item>
<spacer name="verticalSpacer">
Expand All @@ -227,12 +225,9 @@
<x>0</x>
<y>0</y>
<width>800</width>
<height>24</height>
<height>22</height>
</rect>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::PreventContextMenu</enum>
</property>
Expand Down Expand Up @@ -370,9 +365,6 @@
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::PreventContextMenu</enum>
</property>
Expand Down
Loading

0 comments on commit 9e4aec1

Please sign in to comment.