From 5bdbd51fa85f3a4d521b8b28601255e8b62513ce Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sat, 21 Dec 2024 16:21:53 -0800 Subject: [PATCH] implement search bar in shortcuts menu (#5285) * implement search bar in shortcuts menu * remove unneeded imports * use expandAll --- cockatrice/CMakeLists.txt | 1 + cockatrice/src/dialogs/dlg_settings.cpp | 92 +++--------- cockatrice/src/dialogs/dlg_settings.h | 13 +- cockatrice/src/settings/shortcut_treeview.cpp | 132 ++++++++++++++++++ cockatrice/src/settings/shortcut_treeview.h | 34 +++++ 5 files changed, 197 insertions(+), 75 deletions(-) create mode 100644 cockatrice/src/settings/shortcut_treeview.cpp create mode 100644 cockatrice/src/settings/shortcut_treeview.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 4fbba62e2..db81b3d0a 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -126,6 +126,7 @@ set(cockatrice_SOURCES src/settings/settings_manager.cpp src/settings/cache_settings.cpp src/settings/shortcuts_settings.cpp + src/settings/shortcut_treeview.cpp src/client/sound_engine.cpp src/client/network/spoiler_background_updater.cpp src/game/zones/stack_zone.cpp diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp index cc4a0b4bb..bbec08c9b 100644 --- a/cockatrice/src/dialogs/dlg_settings.cpp +++ b/cockatrice/src/dialogs/dlg_settings.cpp @@ -6,10 +6,12 @@ #include "../client/sound_engine.h" #include "../client/ui/picture_loader.h" #include "../client/ui/theme_manager.h" +#include "../deck/custom_line_edit.h" #include "../game/cards/card_database.h" #include "../game/cards/card_database_manager.h" #include "../main.h" #include "../settings/cache_settings.h" +#include "../settings/shortcut_treeview.h" #include "../utility/sequence_edit.h" #include @@ -37,8 +39,6 @@ #include #include #include -#include -#include #define WIKI_CUSTOM_PIC_URL "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Picture-Download-URLs" #define WIKI_CUSTOM_SHORTCUTS "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Keyboard-Shortcuts" @@ -1257,13 +1257,24 @@ void SoundSettingsPage::retranslateUi() ShortcutSettingsPage::ShortcutSettingsPage() { + // search bar + searchEdit = new SearchLineEdit; + searchEdit->setObjectName("searchEdit"); + searchEdit->setPlaceholderText(tr("Search by shortcut name")); + searchEdit->setClearButtonEnabled(true); + + setFocusProxy(searchEdit); + setFocusPolicy(Qt::ClickFocus); + // table - shortcutsTable = new QTreeWidget(); - shortcutsTable->setColumnCount(2); + shortcutsTable = new ShortcutTreeView(this); + shortcutsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - shortcutsTable->setUniformRowHeights(true); - shortcutsTable->setAlternatingRowColors(true); - shortcutsTable->header()->resizeSection(0, shortcutsTable->width() / 3 * 2); + shortcutsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + shortcutsTable->setColumnWidth(0, width() / 3 * 2); + searchEdit->setTreeView(shortcutsTable); + + connect(searchEdit, &SearchLineEdit::textChanged, shortcutsTable, &ShortcutTreeView::updateSearchString); // edit widget currentActionGroupLabel = new QLabel(this); @@ -1303,6 +1314,7 @@ ShortcutSettingsPage::ShortcutSettingsPage() _buttonsLayout->addWidget(btnClearAll); auto *_mainLayout = new QVBoxLayout; + _mainLayout->addWidget(searchEdit); _mainLayout->addWidget(shortcutsTable); _mainLayout->addWidget(editShortcutGroupBox); _mainLayout->addLayout(_buttonsLayout); @@ -1311,21 +1323,17 @@ ShortcutSettingsPage::ShortcutSettingsPage() connect(btnResetAll, SIGNAL(clicked()), this, SLOT(resetShortcuts())); connect(btnClearAll, SIGNAL(clicked()), this, SLOT(clearShortcuts())); - connect(shortcutsTable, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, - SLOT(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *))); - connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts())); - createShortcuts(); + connect(shortcutsTable, &ShortcutTreeView::currentItemChanged, this, &ShortcutSettingsPage::currentItemChanged); } -void ShortcutSettingsPage::currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem * /*previous */) +void ShortcutSettingsPage::currentItemChanged(const QString &key) { - if (current == nullptr) { + if (key.isEmpty()) { currentActionGroupName->setText(""); currentActionName->setText(""); editTextBox->setShortcutName(""); } else { - QString key = current->data(2, Qt::DisplayRole).toString(); QString group = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName(); QString action = SettingsCache::instance().shortcuts().getShortcut(key).getName(); currentActionGroupName->setText(group); @@ -1342,59 +1350,6 @@ void ShortcutSettingsPage::resetShortcuts() } } -void ShortcutSettingsPage::createShortcuts() -{ - QHash parentItems; - QTreeWidgetItem *curParent = nullptr; - for (const auto &key : SettingsCache::instance().shortcuts().getAllShortcutKeys()) { - QString name = SettingsCache::instance().shortcuts().getShortcut(key).getName(); - QString group = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName(); - QString shortcut = SettingsCache::instance().shortcuts().getShortcutString(key); - - if (parentItems.contains(group)) { - curParent = parentItems.value(group); - } else { - curParent = new QTreeWidgetItem((QTreeWidget *)nullptr, QStringList({group, "", ""})); - static QFont font = curParent->font(0); - font.setBold(true); - curParent->setFont(0, font); - parentItems.insert(group, curParent); - } - - new QTreeWidgetItem(curParent, QStringList({name, shortcut, key})); - } - shortcutsTable->clear(); - shortcutsTable->insertTopLevelItems(0, parentItems.values()); - shortcutsTable->setCurrentItem(nullptr); - shortcutsTable->expandAll(); - shortcutsTable->sortItems(0, Qt::AscendingOrder); -} - -void ShortcutSettingsPage::refreshShortcuts() -{ - QTreeWidgetItem *curParent = nullptr; - QTreeWidgetItem *curChild = nullptr; - for (int i = 0; i < shortcutsTable->topLevelItemCount(); ++i) { - curParent = shortcutsTable->topLevelItem(i); - for (int j = 0; j < curParent->childCount(); ++j) { - curChild = curParent->child(j); - QString key = curChild->data(2, Qt::DisplayRole).toString(); - QString name = SettingsCache::instance().shortcuts().getShortcut(key).getName(); - QString shortcut = SettingsCache::instance().shortcuts().getShortcutString(key); - curChild->setText(0, name); - curChild->setText(1, shortcut); - - if (j == 0) { - // the first child also updates the parent's group name - QString group = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName(); - curParent->setText(0, group); - } - } - } - shortcutsTable->sortItems(0, Qt::AscendingOrder); - currentItemChanged(shortcutsTable->currentItem(), nullptr); -} - void ShortcutSettingsPage::clearShortcuts() { if (QMessageBox::question(this, tr("Clear all default shortcuts"), @@ -1405,8 +1360,7 @@ void ShortcutSettingsPage::clearShortcuts() void ShortcutSettingsPage::retranslateUi() { - shortcutsTable->setHeaderLabels(QStringList() << tr("Action") << tr("Shortcut")); - refreshShortcuts(); + shortcutsTable->retranslateUi(); currentActionGroupLabel->setText(tr("Section:")); currentActionLabel->setText(tr("Action:")); diff --git a/cockatrice/src/dialogs/dlg_settings.h b/cockatrice/src/dialogs/dlg_settings.h index c85094581..184f04b53 100644 --- a/cockatrice/src/dialogs/dlg_settings.h +++ b/cockatrice/src/dialogs/dlg_settings.h @@ -11,6 +11,10 @@ #include #include +class ShortcutTreeView; +class SearchLineEdit; +class QTreeView; +class QStandardItemModel; class CardDatabase; class QCloseEvent; class QGridLayout; @@ -21,8 +25,6 @@ class QListWidgetItem; class QRadioButton; class QSlider; class QStackedWidget; -class QTreeWidget; -class QTreeWidgetItem; class QVBoxLayout; class SequenceEdit; @@ -268,7 +270,8 @@ public: void retranslateUi() override; private: - QTreeWidget *shortcutsTable; + SearchLineEdit *searchEdit; + ShortcutTreeView *shortcutsTable; QVBoxLayout *mainLayout; QHBoxLayout *buttonsLayout; QGroupBox *editShortcutGroupBox; @@ -285,10 +288,8 @@ private: private slots: void resetShortcuts(); - void refreshShortcuts(); - void createShortcuts(); void clearShortcuts(); - void currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); + void currentItemChanged(const QString &key); }; class DlgSettings : public QDialog diff --git a/cockatrice/src/settings/shortcut_treeview.cpp b/cockatrice/src/settings/shortcut_treeview.cpp new file mode 100644 index 000000000..78bb60f32 --- /dev/null +++ b/cockatrice/src/settings/shortcut_treeview.cpp @@ -0,0 +1,132 @@ +#include "shortcut_treeview.h" + +#include "cache_settings.h" +#include "shortcuts_settings.h" + +#include +#include + +ShortcutTreeView::ShortcutTreeView(QWidget *parent) : QTreeView(parent) +{ + // model + shortcutsModel = new QStandardItemModel(this); + shortcutsModel->setColumnCount(3); + populateShortcutsModel(); + + // filter proxy + proxyModel = new QSortFilterProxyModel(this); + proxyModel->setRecursiveFilteringEnabled(true); + proxyModel->setSourceModel(shortcutsModel); + proxyModel->setDynamicSortFilter(true); + proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + proxyModel->setFilterKeyColumn(0); + + QTreeView::setModel(proxyModel); + + // treeview + hideColumn(2); + + setUniformRowHeights(true); + setAlternatingRowColors(true); + + setSortingEnabled(true); + proxyModel->sort(0, Qt::AscendingOrder); + + header()->setSectionResizeMode(QHeaderView::Interactive); + header()->setSortIndicator(0, Qt::AscendingOrder); + setSelectionMode(SingleSelection); + setSelectionBehavior(SelectRows); + + expandAll(); + + connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this, + &ShortcutTreeView::refreshShortcuts); +} + +void ShortcutTreeView::populateShortcutsModel() +{ + QHash parentItems; + QStandardItem *curParent = nullptr; + for (const auto &key : SettingsCache::instance().shortcuts().getAllShortcutKeys()) { + QString name = SettingsCache::instance().shortcuts().getShortcut(key).getName(); + QString group = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName(); + QString shortcut = SettingsCache::instance().shortcuts().getShortcutString(key); + + if (parentItems.contains(group)) { + curParent = parentItems.value(group); + } else { + curParent = new QStandardItem(group); + static QFont font = curParent->font(); + font.setBold(true); + curParent->setFont(font); + parentItems.insert(group, curParent); + } + + QList list = {}; + list << new QStandardItem(name) << new QStandardItem(shortcut) << new QStandardItem(key); + curParent->appendRow(list); + } + + for (const auto &parent : parentItems) { + shortcutsModel->appendRow(parent); + } +} + +void ShortcutTreeView::retranslateUi() +{ + shortcutsModel->setHeaderData(0, Qt::Horizontal, tr("Action")); + shortcutsModel->setHeaderData(1, Qt::Horizontal, tr("Shortcut")); + refreshShortcuts(); +} + +/** + * Loops over the model and reloads all rows + */ +static void loopOverModel(QAbstractItemModel *model, const QModelIndex &parent = QModelIndex()) +{ + for (int r = 0; r < model->rowCount(parent); ++r) { + const auto friendlyNameIndex = model->index(r, 0, parent); + + if (model->hasChildren(friendlyNameIndex)) { + const auto childIndex = model->index(0, 2, friendlyNameIndex); + const auto key = model->data(childIndex).toString(); + const auto shortcutGroupName = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName(); + model->setData(friendlyNameIndex, shortcutGroupName); + + loopOverModel(model, friendlyNameIndex); + } else { + const auto shortcutSequenceIndex = model->index(r, 1, parent); + const auto keyIndex = model->index(r, 2, parent); + const auto key = model->data(keyIndex).toString(); + + const auto shortcutKey = SettingsCache::instance().shortcuts().getShortcut(key).getName(); + const auto shortcutSequence = SettingsCache::instance().shortcuts().getShortcutString(key); + model->setData(friendlyNameIndex, shortcutKey); + model->setData(shortcutSequenceIndex, shortcutSequence); + } + } +} + +void ShortcutTreeView::refreshShortcuts() +{ + loopOverModel(shortcutsModel); +} + +void ShortcutTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex & /* previous */) +{ + QTreeView::scrollTo(current, QAbstractItemView::EnsureVisible); + if (current.parent().isValid()) { + auto shortcutName = model()->data(model()->index(current.row(), 2, current.parent())).toString(); + emit currentItemChanged(shortcutName); + } else { + // emit empty string if the selection is a category header + emit currentItemChanged(""); + } +} + +void ShortcutTreeView::updateSearchString(const QString &searchString) +{ + proxyModel->setFilterFixedString(searchString); + expandAll(); +} diff --git a/cockatrice/src/settings/shortcut_treeview.h b/cockatrice/src/settings/shortcut_treeview.h new file mode 100644 index 000000000..a38c5adbf --- /dev/null +++ b/cockatrice/src/settings/shortcut_treeview.h @@ -0,0 +1,34 @@ +#ifndef SHORTCUT_TREEVIEW_H +#define SHORTCUT_TREEVIEW_H + +#include +#include +#include + +class QSortFilterProxyModel; +class ShortcutTreeView : public QTreeView +{ + Q_OBJECT +public: + explicit ShortcutTreeView(QWidget *parent); + void retranslateUi(); + +signals: + void currentItemChanged(const QString &shortcut); + +public slots: + void updateSearchString(const QString &searchString); + +private: + QStandardItemModel *shortcutsModel; + QSortFilterProxyModel *proxyModel; + void populateShortcutsModel(); + +private slots: + void refreshShortcuts(); + +protected: + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override; +}; + +#endif // SHORTCUT_TREEVIEW_H