From 03e0e1e05dec75ccc665f237be3bd6ae9cbca29f Mon Sep 17 00:00:00 2001 From: Oleg Shparber Date: Fri, 3 Jan 2020 00:31:06 -0500 Subject: [PATCH] feat(ui): add tree view navigation with directional keys and Alt Fixes #1007. --- src/libs/browser/webcontrol.cpp | 6 ++- src/libs/browser/webcontrol.h | 1 + src/libs/ui/browsertab.cpp | 4 ++ src/libs/ui/searchsidebar.cpp | 81 ++++++++++++++++++++++----------- src/libs/ui/searchsidebar.h | 6 ++- 5 files changed, 69 insertions(+), 29 deletions(-) diff --git a/src/libs/browser/webcontrol.cpp b/src/libs/browser/webcontrol.cpp index 7bff94941..546124f7d 100644 --- a/src/libs/browser/webcontrol.cpp +++ b/src/libs/browser/webcontrol.cpp @@ -58,6 +58,11 @@ WebControl::WebControl(QWidget *parent) setLayout(layout); } +void WebControl::focus() +{ + m_webView->setFocus(); +} + int WebControl::zoomLevel() const { return m_webView->zoomLevel(); @@ -99,7 +104,6 @@ void WebControl::setWebBridgeObject(const QString &name, QObject *object) void WebControl::load(const QUrl &url) { m_webView->load(url); - m_webView->setFocus(); } void WebControl::activateSearchBar() diff --git a/src/libs/browser/webcontrol.h b/src/libs/browser/webcontrol.h index e026c6951..d0fc377d4 100644 --- a/src/libs/browser/webcontrol.h +++ b/src/libs/browser/webcontrol.h @@ -41,6 +41,7 @@ class WebControl final : public QWidget public: explicit WebControl(QWidget *parent = nullptr); + void focus(); void load(const QUrl &url); bool canGoBack() const; bool canGoForward() const; diff --git a/src/libs/ui/browsertab.cpp b/src/libs/ui/browsertab.cpp index b353dcaf8..6928f473f 100644 --- a/src/libs/ui/browsertab.cpp +++ b/src/libs/ui/browsertab.cpp @@ -161,6 +161,8 @@ BrowserTab *BrowserTab::clone(QWidget *parent) const if (m_searchSidebar) { tab->m_searchSidebar = m_searchSidebar->clone(); + connect(tab->m_searchSidebar, &SearchSidebar::activated, + tab->m_webControl, &Browser::WebControl::focus); connect(tab->m_searchSidebar, &SearchSidebar::navigationRequested, tab->m_webControl, &Browser::WebControl::load); } @@ -189,6 +191,8 @@ SearchSidebar *BrowserTab::searchSidebar() if (m_searchSidebar == nullptr) { // Create SearchSidebar managed by this tab. m_searchSidebar = new SearchSidebar(); + connect(m_searchSidebar, &SearchSidebar::activated, + m_webControl, &Browser::WebControl::focus); connect(m_searchSidebar, &SearchSidebar::navigationRequested, m_webControl, &Browser::WebControl::load); } diff --git a/src/libs/ui/searchsidebar.cpp b/src/libs/ui/searchsidebar.cpp index fd9dd8915..0b82d738c 100644 --- a/src/libs/ui/searchsidebar.cpp +++ b/src/libs/ui/searchsidebar.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -80,8 +81,20 @@ SearchSidebar::SearchSidebar(const SearchSidebar *other, QWidget *parent) delegate->setDecorationRoles({Registry::ItemDataRole::DocsetIconRole, Qt::DecorationRole}); m_treeView->setItemDelegate(delegate); - connect(m_treeView, &QTreeView::activated, this, &SearchSidebar::indexActivated); - connect(m_treeView, &QTreeView::clicked, this, &SearchSidebar::indexActivated); + connect(m_treeView, &QTreeView::activated, this, &SearchSidebar::navigateToIndexAndActivate); + connect(m_treeView, &QTreeView::clicked, this, &SearchSidebar::navigateToIndex); + + // Setup Alt+Up, Alt+Down, etc shortcuts. + const auto keyList = {Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, + Qt::Key_PageUp, Qt::Key_PageDown, + Qt::Key_Home, Qt::Key_End}; + for (const auto key : keyList) { + auto shortcut = new QShortcut(key | Qt::AltModifier, this); + connect(shortcut, &QShortcut::activated, this, [this, key]() { + QKeyEvent event(QKeyEvent::KeyPress, key, Qt::NoModifier); + QCoreApplication::sendEvent(m_treeView, &event); + }); + } // Setup page TOC view. // TODO: Move to a separate Sidebar View. @@ -106,8 +119,8 @@ SearchSidebar::SearchSidebar(const SearchSidebar *other, QWidget *parent) }); m_pageTocView->setModel(m_pageTocModel); - connect(m_pageTocView, &QListView::activated, this, &SearchSidebar::indexActivated); - connect(m_pageTocView, &QListView::clicked, this, &SearchSidebar::indexActivated); + connect(m_pageTocView, &QListView::activated, this, &SearchSidebar::navigateToIndexAndActivate); + connect(m_pageTocView, &QListView::clicked, this, &SearchSidebar::navigateToIndex); // Setup search input box. m_searchEdit = new SearchEdit(); @@ -164,14 +177,7 @@ SearchSidebar::SearchSidebar(const SearchSidebar *other, QWidget *parent) // Connect to the new selection model. connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged, - this, [this](const QItemSelection &selected) { - if (selected.isEmpty()) { - return; - } - - m_delayedNavigationTimer->setProperty("index", selected.indexes().first()); - m_delayedNavigationTimer->start(); - }); + this, &SearchSidebar::navigateToSelectionWithDelay); } m_treeView->reset(); @@ -213,10 +219,7 @@ SearchSidebar::SearchSidebar(const SearchSidebar *other, QWidget *parent) return; } - indexActivated(index); - - // Get focus back. - m_searchEdit->setFocus(Qt::MouseFocusReason); + navigateToIndex(index); }); // Setup Docset Registry. @@ -274,14 +277,7 @@ void SearchSidebar::setTreeViewModel(QAbstractItemModel *model, bool isRootDecor // Connect to the new selection model. connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged, - this, [this](const QItemSelection &selected) { - if (selected.isEmpty()) { - return; - } - - m_delayedNavigationTimer->setProperty("index", selected.indexes().first()); - m_delayedNavigationTimer->start(); - }); + this, &SearchSidebar::navigateToSelectionWithDelay); } } @@ -315,13 +311,41 @@ void SearchSidebar::search(const Registry::SearchQuery &query) m_searchEdit->setText(query.toString()); } -void SearchSidebar::indexActivated(const QModelIndex &index) +void SearchSidebar::navigateToIndex(const QModelIndex &index) +{ + // When triggered by click, cancel delayed navigation request caused by the selection change. + if (m_delayedNavigationTimer->isActive() + && m_delayedNavigationTimer->property("index").toModelIndex() == index) { + m_delayedNavigationTimer->stop(); + } + + const QVariant url = index.data(Registry::ItemDataRole::UrlRole); + if (url.isNull()) { + return; + } + + emit navigationRequested(url.toUrl()); +} + +void SearchSidebar::navigateToIndexAndActivate(const QModelIndex &index) { const QVariant url = index.data(Registry::ItemDataRole::UrlRole); - if (url.isNull()) + if (url.isNull()) { return; + } emit navigationRequested(url.toUrl()); + emit activated(); +} + +void SearchSidebar::navigateToSelectionWithDelay(const QItemSelection &selection) +{ + if (selection.isEmpty()) { + return; + } + + m_delayedNavigationTimer->setProperty("index", selection.indexes().first()); + m_delayedNavigationTimer->start(); } void SearchSidebar::setupSearchBoxCompletions() @@ -347,6 +371,9 @@ bool SearchSidebar::eventFilter(QObject *object, QEvent *event) if (object == m_searchEdit && event->type() == QEvent::KeyPress) { auto e = static_cast(event); switch (e->key()) { + case Qt::Key_Return: + emit activated(); + break; case Qt::Key_Home: case Qt::Key_End: case Qt::Key_Left: @@ -355,13 +382,13 @@ bool SearchSidebar::eventFilter(QObject *object, QEvent *event) break; } [[clang::fallthrough]]; - case Qt::Key_Return: case Qt::Key_Down: case Qt::Key_Up: case Qt::Key_PageDown: case Qt::Key_PageUp: QCoreApplication::sendEvent(m_treeView, event); break; + } } diff --git a/src/libs/ui/searchsidebar.h b/src/libs/ui/searchsidebar.h index 5ddbaee1b..1be802cfb 100644 --- a/src/libs/ui/searchsidebar.h +++ b/src/libs/ui/searchsidebar.h @@ -28,6 +28,7 @@ #include #include +class QItemSelection; class QSplitter; class QListView; class QTimer; @@ -54,6 +55,7 @@ class SearchSidebar final : public Sidebar::View Registry::SearchModel *pageTocModel() const; signals: + void activated(); void navigationRequested(const QUrl &url); public slots: @@ -61,7 +63,9 @@ public slots: void search(const Registry::SearchQuery &query); private slots: - void indexActivated(const QModelIndex &index); + void navigateToIndex(const QModelIndex &index); + void navigateToIndexAndActivate(const QModelIndex &index); + void navigateToSelectionWithDelay(const QItemSelection &selection); void setupSearchBoxCompletions(); protected: