From f516e298cc81badd4dec64ca6061185028c8b3ff Mon Sep 17 00:00:00 2001 From: David Griswold Date: Thu, 2 Apr 2026 15:39:04 +0300 Subject: [PATCH] improve keyboard navigation of game list in three ways: 1) Return key on mac now successfully launches a game 2) up and down arrow keys now move focus out of search field and into game list 3) whenever focus goes into game list it ensures a game is selected --- src/citra_qt/game_list.cpp | 41 ++++++++++++++++++++++++++++++++++++-- src/citra_qt/game_list.h | 3 ++- src/citra_qt/game_list_p.h | 7 +++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index f34753ca7..7713f18f1 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -44,6 +45,36 @@ #include "core/hle/service/fs/archive.h" #include "qcursor.h" +// On Mac OS X, the Return key does not trigger the activation signal for some reason. Fix it. + +void GameListTreeView::keyPressEvent(QKeyEvent* event) { + if (event->key() == Qt::Key_Return) { + const QModelIndex idx = currentIndex(); + if (idx.isValid()) { + emit activated(idx); + return; + } + } + QTreeView::keyPressEvent(event); +} + +// if focus goes into the game list and nothing is already selected, select the first actual game +void GameListTreeView::focusInEvent(QFocusEvent* event) { + if (!currentIndex().isValid()) { + for (int i = 0; i < model()->rowCount(); i++) { + const QModelIndex folder = model()->index(i, 0); + if (model()->rowCount(folder) > 0) { + const QModelIndex first = model()->index(0, 0, folder); + setCurrentIndex(first); + selectionModel()->select(first, + QItemSelectionModel::Select | QItemSelectionModel::Rows); + break; + } + } + } + QTreeView::focusInEvent(event); +} + GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent) : QObject(parent), gamelist{gamelist} {} @@ -88,6 +119,13 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve } break; } + // up, down, tab + // If the up arrow, down arrow, or tab key is pressed, this should move focus to the + // treeview + case Qt::Key_Up: + case Qt::Key_Down: { + gamelist->tree_view->setFocus(); + } default: return QObject::eventFilter(obj, event); } @@ -355,7 +393,7 @@ GameList::GameList(PlayTime::PlayTimeManager& play_time_manager_, GMainWindow* p this->main_window = parent; layout = new QVBoxLayout; - tree_view = new QTreeView; + tree_view = new GameListTreeView; search_field = new GameListSearchField(this); item_model = new QStandardItemModel(tree_view); tree_view->setModel(item_model); @@ -371,7 +409,6 @@ GameList::GameList(PlayTime::PlayTimeManager& play_time_manager_, GMainWindow* p tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }")); tree_view->setItemDelegateForColumn(0, new CartridgeIconDelegate(tree_view)); tree_view->header()->setContextMenuPolicy(Qt::CustomContextMenu); - UpdateColumnVisibility(); item_model->insertColumns(0, COLUMN_COUNT); diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h index 25348d86b..5de9d43f1 100644 --- a/src/citra_qt/game_list.h +++ b/src/citra_qt/game_list.h @@ -21,6 +21,7 @@ enum class MediaType : u32; class GameListWorker; class GameListDir; class GameListSearchField; +class GameListTreeView; class GMainWindow; class QFileSystemWatcher; class QHBoxLayout; @@ -140,7 +141,7 @@ private: GameListSearchField* search_field; GMainWindow* main_window = nullptr; QVBoxLayout* layout = nullptr; - QTreeView* tree_view = nullptr; + GameListTreeView* tree_view = nullptr; QStandardItemModel* item_model = nullptr; GameListWorker* current_worker = nullptr; QFileSystemWatcher* watcher = nullptr; diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h index 45df2a483..d74170749 100644 --- a/src/citra_qt/game_list_p.h +++ b/src/citra_qt/game_list_p.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "citra_qt/uisettings.h" #include "citra_qt/util/util.h" @@ -529,3 +530,9 @@ private: QLabel* label_filter_result = nullptr; QToolButton* button_filter_close = nullptr; }; + +class GameListTreeView : public QTreeView { +protected: + void keyPressEvent(QKeyEvent* event) override; + void focusInEvent(QFocusEvent* event) override; +};