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
327351void 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
0 commit comments