Skip to content

Commit 03e0e1e

Browse files
committed
feat(ui): add tree view navigation with directional keys and Alt
Fixes #1007.
1 parent 6968e8e commit 03e0e1e

File tree

5 files changed

+69
-29
lines changed

5 files changed

+69
-29
lines changed

src/libs/browser/webcontrol.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ WebControl::WebControl(QWidget *parent)
5858
setLayout(layout);
5959
}
6060

61+
void WebControl::focus()
62+
{
63+
m_webView->setFocus();
64+
}
65+
6166
int WebControl::zoomLevel() const
6267
{
6368
return m_webView->zoomLevel();
@@ -99,7 +104,6 @@ void WebControl::setWebBridgeObject(const QString &name, QObject *object)
99104
void WebControl::load(const QUrl &url)
100105
{
101106
m_webView->load(url);
102-
m_webView->setFocus();
103107
}
104108

105109
void WebControl::activateSearchBar()

src/libs/browser/webcontrol.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class WebControl final : public QWidget
4141
public:
4242
explicit WebControl(QWidget *parent = nullptr);
4343

44+
void focus();
4445
void load(const QUrl &url);
4546
bool canGoBack() const;
4647
bool canGoForward() const;

src/libs/ui/browsertab.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ BrowserTab *BrowserTab::clone(QWidget *parent) const
161161

162162
if (m_searchSidebar) {
163163
tab->m_searchSidebar = m_searchSidebar->clone();
164+
connect(tab->m_searchSidebar, &SearchSidebar::activated,
165+
tab->m_webControl, &Browser::WebControl::focus);
164166
connect(tab->m_searchSidebar, &SearchSidebar::navigationRequested,
165167
tab->m_webControl, &Browser::WebControl::load);
166168
}
@@ -189,6 +191,8 @@ SearchSidebar *BrowserTab::searchSidebar()
189191
if (m_searchSidebar == nullptr) {
190192
// Create SearchSidebar managed by this tab.
191193
m_searchSidebar = new SearchSidebar();
194+
connect(m_searchSidebar, &SearchSidebar::activated,
195+
m_webControl, &Browser::WebControl::focus);
192196
connect(m_searchSidebar, &SearchSidebar::navigationRequested,
193197
m_webControl, &Browser::WebControl::load);
194198
}

src/libs/ui/searchsidebar.cpp

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include <QKeyEvent>
4242
#include <QListView>
4343
#include <QScrollBar>
44+
#include <QShortcut>
4445
#include <QSplitter>
4546
#include <QTimer>
4647
#include <QTreeView>
@@ -80,8 +81,20 @@ SearchSidebar::SearchSidebar(const SearchSidebar *other, QWidget *parent)
8081
delegate->setDecorationRoles({Registry::ItemDataRole::DocsetIconRole, Qt::DecorationRole});
8182
m_treeView->setItemDelegate(delegate);
8283

83-
connect(m_treeView, &QTreeView::activated, this, &SearchSidebar::indexActivated);
84-
connect(m_treeView, &QTreeView::clicked, this, &SearchSidebar::indexActivated);
84+
connect(m_treeView, &QTreeView::activated, this, &SearchSidebar::navigateToIndexAndActivate);
85+
connect(m_treeView, &QTreeView::clicked, this, &SearchSidebar::navigateToIndex);
86+
87+
// Setup Alt+Up, Alt+Down, etc shortcuts.
88+
const auto keyList = {Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right,
89+
Qt::Key_PageUp, Qt::Key_PageDown,
90+
Qt::Key_Home, Qt::Key_End};
91+
for (const auto key : keyList) {
92+
auto shortcut = new QShortcut(key | Qt::AltModifier, this);
93+
connect(shortcut, &QShortcut::activated, this, [this, key]() {
94+
QKeyEvent event(QKeyEvent::KeyPress, key, Qt::NoModifier);
95+
QCoreApplication::sendEvent(m_treeView, &event);
96+
});
97+
}
8598

8699
// Setup page TOC view.
87100
// TODO: Move to a separate Sidebar View.
@@ -106,8 +119,8 @@ SearchSidebar::SearchSidebar(const SearchSidebar *other, QWidget *parent)
106119
});
107120
m_pageTocView->setModel(m_pageTocModel);
108121

109-
connect(m_pageTocView, &QListView::activated, this, &SearchSidebar::indexActivated);
110-
connect(m_pageTocView, &QListView::clicked, this, &SearchSidebar::indexActivated);
122+
connect(m_pageTocView, &QListView::activated, this, &SearchSidebar::navigateToIndexAndActivate);
123+
connect(m_pageTocView, &QListView::clicked, this, &SearchSidebar::navigateToIndex);
111124

112125
// Setup search input box.
113126
m_searchEdit = new SearchEdit();
@@ -164,14 +177,7 @@ SearchSidebar::SearchSidebar(const SearchSidebar *other, QWidget *parent)
164177

165178
// Connect to the new selection model.
166179
connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
167-
this, [this](const QItemSelection &selected) {
168-
if (selected.isEmpty()) {
169-
return;
170-
}
171-
172-
m_delayedNavigationTimer->setProperty("index", selected.indexes().first());
173-
m_delayedNavigationTimer->start();
174-
});
180+
this, &SearchSidebar::navigateToSelectionWithDelay);
175181
}
176182

177183
m_treeView->reset();
@@ -213,10 +219,7 @@ SearchSidebar::SearchSidebar(const SearchSidebar *other, QWidget *parent)
213219
return;
214220
}
215221

216-
indexActivated(index);
217-
218-
// Get focus back.
219-
m_searchEdit->setFocus(Qt::MouseFocusReason);
222+
navigateToIndex(index);
220223
});
221224

222225
// Setup Docset Registry.
@@ -274,14 +277,7 @@ void SearchSidebar::setTreeViewModel(QAbstractItemModel *model, bool isRootDecor
274277

275278
// Connect to the new selection model.
276279
connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
277-
this, [this](const QItemSelection &selected) {
278-
if (selected.isEmpty()) {
279-
return;
280-
}
281-
282-
m_delayedNavigationTimer->setProperty("index", selected.indexes().first());
283-
m_delayedNavigationTimer->start();
284-
});
280+
this, &SearchSidebar::navigateToSelectionWithDelay);
285281
}
286282
}
287283

@@ -315,13 +311,41 @@ void SearchSidebar::search(const Registry::SearchQuery &query)
315311
m_searchEdit->setText(query.toString());
316312
}
317313

318-
void SearchSidebar::indexActivated(const QModelIndex &index)
314+
void SearchSidebar::navigateToIndex(const QModelIndex &index)
315+
{
316+
// When triggered by click, cancel delayed navigation request caused by the selection change.
317+
if (m_delayedNavigationTimer->isActive()
318+
&& m_delayedNavigationTimer->property("index").toModelIndex() == index) {
319+
m_delayedNavigationTimer->stop();
320+
}
321+
322+
const QVariant url = index.data(Registry::ItemDataRole::UrlRole);
323+
if (url.isNull()) {
324+
return;
325+
}
326+
327+
emit navigationRequested(url.toUrl());
328+
}
329+
330+
void SearchSidebar::navigateToIndexAndActivate(const QModelIndex &index)
319331
{
320332
const QVariant url = index.data(Registry::ItemDataRole::UrlRole);
321-
if (url.isNull())
333+
if (url.isNull()) {
322334
return;
335+
}
323336

324337
emit navigationRequested(url.toUrl());
338+
emit activated();
339+
}
340+
341+
void SearchSidebar::navigateToSelectionWithDelay(const QItemSelection &selection)
342+
{
343+
if (selection.isEmpty()) {
344+
return;
345+
}
346+
347+
m_delayedNavigationTimer->setProperty("index", selection.indexes().first());
348+
m_delayedNavigationTimer->start();
325349
}
326350

327351
void SearchSidebar::setupSearchBoxCompletions()
@@ -347,6 +371,9 @@ bool SearchSidebar::eventFilter(QObject *object, QEvent *event)
347371
if (object == m_searchEdit && event->type() == QEvent::KeyPress) {
348372
auto e = static_cast<QKeyEvent *>(event);
349373
switch (e->key()) {
374+
case Qt::Key_Return:
375+
emit activated();
376+
break;
350377
case Qt::Key_Home:
351378
case Qt::Key_End:
352379
case Qt::Key_Left:
@@ -355,13 +382,13 @@ bool SearchSidebar::eventFilter(QObject *object, QEvent *event)
355382
break;
356383
}
357384
[[clang::fallthrough]];
358-
case Qt::Key_Return:
359385
case Qt::Key_Down:
360386
case Qt::Key_Up:
361387
case Qt::Key_PageDown:
362388
case Qt::Key_PageUp:
363389
QCoreApplication::sendEvent(m_treeView, event);
364390
break;
391+
365392
}
366393
}
367394

src/libs/ui/searchsidebar.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <QModelIndexList>
2929
#include <QWidget>
3030

31+
class QItemSelection;
3132
class QSplitter;
3233
class QListView;
3334
class QTimer;
@@ -54,14 +55,17 @@ class SearchSidebar final : public Sidebar::View
5455
Registry::SearchModel *pageTocModel() const;
5556

5657
signals:
58+
void activated();
5759
void navigationRequested(const QUrl &url);
5860

5961
public slots:
6062
void focusSearchEdit(bool clear = false);
6163
void search(const Registry::SearchQuery &query);
6264

6365
private slots:
64-
void indexActivated(const QModelIndex &index);
66+
void navigateToIndex(const QModelIndex &index);
67+
void navigateToIndexAndActivate(const QModelIndex &index);
68+
void navigateToSelectionWithDelay(const QItemSelection &selection);
6569
void setupSearchBoxCompletions();
6670

6771
protected:

0 commit comments

Comments
 (0)