diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index c2bd98ac4..4b6579e71 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -52,7 +52,6 @@ #include #include -#include #include #include #include @@ -61,11 +60,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -263,7 +264,7 @@ public: QVector all_toolbuttons; QWidget *centralWidget = nullptr; - QHBoxLayout *horizontalLayout = nullptr; + QVBoxLayout *verticalLayout = nullptr; QMenuBar *menuBar = nullptr; QMenu *fileMenu; @@ -804,11 +805,11 @@ public: centralWidget = new QWidget(MainWindow); centralWidget->setObjectName(QStringLiteral("centralWidget")); - horizontalLayout = new QHBoxLayout(centralWidget); - horizontalLayout->setSpacing(0); - horizontalLayout->setObjectName(QStringLiteral("horizontalLayout")); - horizontalLayout->setSizeConstraint(QLayout::SetDefaultConstraint); - horizontalLayout->setContentsMargins(0, 0, 0, 0); + verticalLayout = new QVBoxLayout(centralWidget); + verticalLayout->setSpacing(0); + verticalLayout->setObjectName(QStringLiteral("horizontalLayout")); + verticalLayout->setSizeConstraint(QLayout::SetDefaultConstraint); + verticalLayout->setContentsMargins(0, 0, 0, 0); MainWindow->setCentralWidget(centralWidget); createNewsToolbar(MainWindow); @@ -870,13 +871,21 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow updateNewsLabel(); } + // Create instance list filter box + { + filterView = new QLineEdit(ui->centralWidget); + + ui->verticalLayout->addWidget(filterView); + } + // Create the instance list widget { view = new InstanceView(ui->centralWidget, APPLICATION->instances().get()); connect(view, &InstanceView::showContextMenu, this, &MainWindow::showInstanceContextMenu); + connect(filterView, &QLineEdit::textEdited, view, &InstanceView::setFilterQuery); - ui->horizontalLayout->addWidget(view); + ui->verticalLayout->addWidget(view); } // The cat background { diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 82a6ea77d..45e15a4fe 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -55,6 +55,7 @@ class NewsChecker; class QToolButton; class LabeledToolButton; class QLabel; +class QLineEdit; class InstanceView; class MinecraftLauncher; class BaseProfilerFactory; @@ -220,6 +221,7 @@ private: // these are managed by Qt's memory management model! InstanceView *view = nullptr; + QLineEdit *filterView = nullptr; QToolButton *newsLabel = nullptr; QMenu *accountMenu = nullptr; QToolButton *accountMenuButton = nullptr; diff --git a/launcher/ui/instanceview/InstanceTableProxyModel.cpp b/launcher/ui/instanceview/InstanceTableProxyModel.cpp index 6c6e7850f..955dc0ca9 100644 --- a/launcher/ui/instanceview/InstanceTableProxyModel.cpp +++ b/launcher/ui/instanceview/InstanceTableProxyModel.cpp @@ -20,6 +20,7 @@ #include "InstanceList.h" #include +#include #include InstanceTableProxyModel::InstanceTableProxyModel(QObject* parent) : QSortFilterProxyModel(parent) @@ -79,3 +80,58 @@ QVariant InstanceTableProxyModel::data(const QModelIndex& index, int role) const } return data; } + +void InstanceTableProxyModel::setFilterQuery(const QString query) +{ + QList foo = parseFilterQuery(query); + setFilterQuery(foo); +} + +void InstanceTableProxyModel::setFilterQuery(const QList query) +{ + m_filter = query; + invalidateFilter(); +} + +bool InstanceTableProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const +{ + if (m_filter.isEmpty()) + return true; + + for (const InstanceFilterQuery& q : m_filter) { + const InstanceList::Column c = q.first; + const QString query = q.second; + + QModelIndex index = sourceModel()->index(sourceRow, c, sourceParent); + QString content = sourceModel()->data(index).toString().toLower(); + + if (!query.isNull() && !content.contains(query)) + return false; + } + return true; +} + +QList InstanceTableProxyModel::parseFilterQuery(QString query) +{ + const QRegularExpression pattern = QRegularExpression("(?:(?\\w+):)?(?:(?\\w+)|\"(?.+)\")"); + QList queries; + + QRegularExpressionMatchIterator it = pattern.globalMatch(query); + while (it.hasNext()) { + const QRegularExpressionMatch& match = it.next(); + InstanceList::Column c = InstanceList::NameColumn; + + QString prefix = match.captured("prefix"); + if (prefix.toLower() == "category") + c = InstanceList::CategoryColumn; + else if (prefix.toLower() == "version") + c = InstanceList::GameVersionColumn; + + QString content = match.captured("content1"); + if (content.isNull()) + content = match.captured("content2"); + + queries << std::make_pair(c, content.toLower()); + } + return queries; +} diff --git a/launcher/ui/instanceview/InstanceTableProxyModel.h b/launcher/ui/instanceview/InstanceTableProxyModel.h index 73508f389..40105728e 100644 --- a/launcher/ui/instanceview/InstanceTableProxyModel.h +++ b/launcher/ui/instanceview/InstanceTableProxyModel.h @@ -15,18 +15,29 @@ #pragma once +#include "InstanceList.h" + #include #include +typedef std::pair InstanceFilterQuery; + class InstanceTableProxyModel : public QSortFilterProxyModel { Q_OBJECT public: InstanceTableProxyModel(QObject* parent = 0); + void setFilterQuery(const QString query); + void setFilterQuery(const QList query); + + static QList parseFilterQuery(const QString query); + protected: QVariant data(const QModelIndex& index, int role) const override; + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const; private: QCollator m_naturalSort; + QList m_filter; }; diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 0aac3b9d5..622e0501f 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -221,6 +221,14 @@ QModelIndex InstanceView::mappedIndex(const QModelIndex& index) const return m_tableProxy->mapToSource(index); } +void InstanceView::setFilterQuery(const QString& query) +{ + if (m_displayMode == DisplayMode::GridMode) { + return m_gridProxy->setFilterQuery(query); + } + return m_tableProxy->setFilterQuery(query); +} + void InstanceView::setCatDisplayed(bool enabled) { if (enabled) { diff --git a/launcher/ui/instanceview/InstanceView.h b/launcher/ui/instanceview/InstanceView.h index 6fb38ddbc..27e418094 100644 --- a/launcher/ui/instanceview/InstanceView.h +++ b/launcher/ui/instanceview/InstanceView.h @@ -51,6 +51,7 @@ class InstanceView : public QStackedWidget { void storeState(); void setCatDisplayed(bool enabled); + void setFilterQuery(const QString& query); void editSelected(InstanceList::Column targetColumn = InstanceList::NameColumn);