From 2e34464948c7a2f32ba8aceb5d6be23fd39f6fc6 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 2 Oct 2022 11:12:14 +0200 Subject: [PATCH] feat: prototype grid view Signed-off-by: Sefa Eyeoglu --- launcher/CMakeLists.txt | 6 +- launcher/ui/MainWindow.h | 1 - launcher/ui/instanceview/InstanceDelegate.cpp | 7 +- launcher/ui/instanceview/InstanceDelegate.h | 5 +- .../instanceview/InstanceGridProxyModel.cpp | 26 +++++++ .../ui/instanceview/InstanceGridProxyModel.h | 28 +++++++ ...yModel.cpp => InstanceTableProxyModel.cpp} | 7 +- ...ProxyModel.h => InstanceTableProxyModel.h} | 4 +- launcher/ui/instanceview/InstanceView.cpp | 74 +++++++++++++++---- launcher/ui/instanceview/InstanceView.h | 23 +++++- 10 files changed, 148 insertions(+), 33 deletions(-) create mode 100644 launcher/ui/instanceview/InstanceGridProxyModel.cpp create mode 100644 launcher/ui/instanceview/InstanceGridProxyModel.h rename launcher/ui/instanceview/{InstanceProxyModel.cpp => InstanceTableProxyModel.cpp} (86%) rename launcher/ui/instanceview/{InstanceProxyModel.h => InstanceTableProxyModel.h} (88%) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 75f1f4b82..78cbb7bb7 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -866,8 +866,10 @@ SET(LAUNCHER_SOURCES ui/widgets/WideBar.cpp # GUI - instance group view - ui/instanceview/InstanceProxyModel.cpp - ui/instanceview/InstanceProxyModel.h + ui/instanceview/InstanceTableProxyModel.cpp + ui/instanceview/InstanceTableProxyModel.h + ui/instanceview/InstanceGridProxyModel.cpp + ui/instanceview/InstanceGridProxyModel.h ui/instanceview/InstanceDelegate.h ui/instanceview/InstanceDelegate.cpp ui/instanceview/InstanceView.h diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 6484bb985..82a6ea77d 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -53,7 +53,6 @@ class LaunchController; class NewsChecker; class QToolButton; -class InstanceProxyModel; class LabeledToolButton; class QLabel; class InstanceView; diff --git a/launcher/ui/instanceview/InstanceDelegate.cpp b/launcher/ui/instanceview/InstanceDelegate.cpp index 1e5124891..b937a5eaf 100644 --- a/launcher/ui/instanceview/InstanceDelegate.cpp +++ b/launcher/ui/instanceview/InstanceDelegate.cpp @@ -19,15 +19,12 @@ #include "InstanceDelegate.h" #include "InstanceList.h" -InstanceDelegate::InstanceDelegate(QObject* parent) : QStyledItemDelegate(parent) {} +InstanceDelegate::InstanceDelegate(QObject* parent, int iconSize) : QStyledItemDelegate(parent), m_iconSize(iconSize) {} void InstanceDelegate::initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const { QStyledItemDelegate::initStyleOption(option, index); if (index.column() == InstanceList::NameColumn) { - // make decoration fill cell, subtract default margins - QSize decorationSize = QSize(option->rect.height(), option->rect.height()); - decorationSize -= QSize(2, 2); // subtract 1px margin - option->decorationSize = decorationSize; + option->decorationSize = QSize(m_iconSize, m_iconSize);; } } \ No newline at end of file diff --git a/launcher/ui/instanceview/InstanceDelegate.h b/launcher/ui/instanceview/InstanceDelegate.h index f04a6a10a..b745335b4 100644 --- a/launcher/ui/instanceview/InstanceDelegate.h +++ b/launcher/ui/instanceview/InstanceDelegate.h @@ -24,7 +24,10 @@ class InstanceDelegate : public QStyledItemDelegate { Q_OBJECT public: - InstanceDelegate(QObject* parent = 0); + InstanceDelegate(QObject* parent = 0, int iconSize = 48); void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const override; + + private: + int m_iconSize; }; diff --git a/launcher/ui/instanceview/InstanceGridProxyModel.cpp b/launcher/ui/instanceview/InstanceGridProxyModel.cpp new file mode 100644 index 000000000..cb9812e84 --- /dev/null +++ b/launcher/ui/instanceview/InstanceGridProxyModel.cpp @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "InstanceGridProxyModel.h" + +#include +#include "Application.h" +#include "InstanceList.h" + +// Placeholder model, as we might need this in the future +InstanceGridProxyModel::InstanceGridProxyModel(QObject* parent) : InstanceTableProxyModel(parent) {} diff --git a/launcher/ui/instanceview/InstanceGridProxyModel.h b/launcher/ui/instanceview/InstanceGridProxyModel.h new file mode 100644 index 000000000..86b89ee1c --- /dev/null +++ b/launcher/ui/instanceview/InstanceGridProxyModel.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "InstanceTableProxyModel.h" + +class InstanceGridProxyModel : public InstanceTableProxyModel { + Q_OBJECT + + public: + InstanceGridProxyModel(QObject* parent = 0); +}; diff --git a/launcher/ui/instanceview/InstanceProxyModel.cpp b/launcher/ui/instanceview/InstanceTableProxyModel.cpp similarity index 86% rename from launcher/ui/instanceview/InstanceProxyModel.cpp rename to launcher/ui/instanceview/InstanceTableProxyModel.cpp index 804d3784b..00ad69bdd 100644 --- a/launcher/ui/instanceview/InstanceProxyModel.cpp +++ b/launcher/ui/instanceview/InstanceTableProxyModel.cpp @@ -13,22 +13,21 @@ * limitations under the License. */ -#include "InstanceProxyModel.h" +#include "InstanceTableProxyModel.h" #include #include "Application.h" #include "InstanceList.h" -InstanceProxyModel::InstanceProxyModel(QObject* parent) : QSortFilterProxyModel(parent) +InstanceTableProxyModel::InstanceTableProxyModel(QObject* parent) : QSortFilterProxyModel(parent) { m_naturalSort.setNumericMode(true); m_naturalSort.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive); // FIXME: use loaded translation as source of locale instead, hook this up to translation changes m_naturalSort.setLocale(QLocale::system()); - setSortRole(InstanceList::SortRole); } -QVariant InstanceProxyModel::data(const QModelIndex& index, int role) const +QVariant InstanceTableProxyModel::data(const QModelIndex& index, int role) const { QVariant data = QSortFilterProxyModel::data(index, role); if (role == Qt::DecorationRole) { diff --git a/launcher/ui/instanceview/InstanceProxyModel.h b/launcher/ui/instanceview/InstanceTableProxyModel.h similarity index 88% rename from launcher/ui/instanceview/InstanceProxyModel.h rename to launcher/ui/instanceview/InstanceTableProxyModel.h index d9e8ada7f..73508f389 100644 --- a/launcher/ui/instanceview/InstanceProxyModel.h +++ b/launcher/ui/instanceview/InstanceTableProxyModel.h @@ -18,11 +18,11 @@ #include #include -class InstanceProxyModel : public QSortFilterProxyModel { +class InstanceTableProxyModel : public QSortFilterProxyModel { Q_OBJECT public: - InstanceProxyModel(QObject* parent = 0); + InstanceTableProxyModel(QObject* parent = 0); protected: QVariant data(const QModelIndex& index, int role) const override; diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 8fb7ea63d..2acfa332f 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -27,7 +27,8 @@ #include "InstanceDelegate.h" #include "InstanceList.h" -#include "ui/instanceview/InstanceProxyModel.h" +#include "ui/instanceview/InstanceTableProxyModel.h" +#include "ui/instanceview/InstanceGridProxyModel.h" #include #include @@ -36,9 +37,11 @@ InstanceView::InstanceView(QWidget* parent, InstanceList* instances) : QStackedW { prepareModel(); createTable(); + createGrid(); addWidget(m_table); - setCurrentWidget(m_table); + addWidget(m_grid); + switchDisplayMode(InstanceView::GridMode); } void InstanceView::storeState() @@ -46,18 +49,33 @@ void InstanceView::storeState() APPLICATION->settings()->set("InstanceViewTableHeaderState", m_table->horizontalHeader()->saveState().toBase64()); } +void InstanceView::switchDisplayMode(InstanceView::DisplayMode mode) +{ + m_displayMode = mode; + if (mode == DisplayMode::TableMode) { + setCurrentWidget(m_table); + } else { + setCurrentWidget(m_grid); + } +} + + void InstanceView::prepareModel() { - m_proxy = new InstanceProxyModel(this); - m_proxy->setSortCaseSensitivity(Qt::CaseInsensitive); - m_proxy->setSourceModel(m_instances); - connect(m_proxy, &QAbstractItemModel::dataChanged, this, &InstanceView::dataChanged); + m_tableProxy = new InstanceTableProxyModel(this); + m_tableProxy->setSortCaseSensitivity(Qt::CaseInsensitive); + m_tableProxy->setSourceModel(m_instances); + m_tableProxy->setSortRole(InstanceList::SortRole); + connect(m_tableProxy, &QAbstractItemModel::dataChanged, this, &InstanceView::dataChanged); + m_gridProxy = new InstanceGridProxyModel(this); + m_gridProxy->setSourceModel(m_instances); + connect(m_tableProxy, &QAbstractItemModel::dataChanged, this, &InstanceView::dataChanged); } void InstanceView::createTable() { m_table = new QTableView(this); - m_table->setModel(m_proxy); + m_table->setModel(m_tableProxy); m_table->setItemDelegate(new InstanceDelegate(this)); m_table->setTabKeyNavigation(false); @@ -83,7 +101,7 @@ void InstanceView::createTable() header->setSectionResizeMode(InstanceList::GameVersionColumn, QHeaderView::Interactive); header->setSectionResizeMode(InstanceList::PlayTimeColumn, QHeaderView::Interactive); header->setSectionResizeMode(InstanceList::LastPlayedColumn, QHeaderView::Interactive); - m_table->verticalHeader()->setDefaultSectionSize(m_rowHeight + 1 + 1); // padding top and bottom + m_table->verticalHeader()->setDefaultSectionSize(m_iconSize + 1 + 1); // padding top and bottom if (APPLICATION->settings()->get("InstanceViewTableHeaderState").toString().isEmpty()) { m_table->setColumnWidth(InstanceList::GameVersionColumn, 96 + 3 + 3); @@ -100,9 +118,34 @@ void InstanceView::createTable() connect(m_table, &QWidget::customContextMenuRequested, this, &InstanceView::contextMenuRequested); } +void InstanceView::createGrid() +{ + m_grid = new QListView(this); + m_grid->setModel(m_gridProxy); + m_grid->setModelColumn(InstanceList::NameColumn); + m_grid->setItemDelegate(new InstanceDelegate(this)); + + m_grid->setSelectionMode(QAbstractItemView::SingleSelection); + m_grid->setSelectionBehavior(QAbstractItemView::SelectRows); + m_grid->setEditTriggers(QAbstractItemView::EditKeyPressed); + m_grid->setCurrentIndex(QModelIndex()); + m_grid->setContextMenuPolicy(Qt::CustomContextMenu); + m_grid->setWordWrap(true); + m_grid->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_grid->setViewMode(QListView::IconMode); + m_grid->setMovement(QListView::Static); + m_grid->setResizeMode(QListView::Adjust); + m_grid->setFrameStyle(QFrame::NoFrame); + m_grid->setGridSize(QSize(m_iconSize * 2, m_iconSize * 2)); + + connect(m_grid, &QAbstractItemView::doubleClicked, this, &InstanceView::activateInstance); + connect(m_grid->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &InstanceView::currentRowChanged); + connect(m_grid, &QWidget::customContextMenuRequested, this, &InstanceView::contextMenuRequested); +} + InstancePtr InstanceView::currentInstance() { - auto current = m_table->selectionModel()->currentIndex(); + auto current = currentView()->selectionModel()->currentIndex(); if (current.isValid()) { int row = mappedIndex(current).row(); return m_instances->at(row); @@ -135,13 +178,13 @@ void InstanceView::currentRowChanged(const QModelIndex& current, const QModelInd void InstanceView::selectNameColumn(const QModelIndex& current, const QModelIndex& previous) { // Make sure Name column is always selected - m_table->setCurrentIndex(current.siblingAtColumn(InstanceList::NameColumn)); + currentView()->setCurrentIndex(current.siblingAtColumn(InstanceList::NameColumn)); } void InstanceView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { // Notify others if data of the current instance changed - auto current = m_table->selectionModel()->currentIndex(); + auto current = currentView()->selectionModel()->currentIndex(); QItemSelection foo(topLeft, bottomRight); if (foo.contains(current)) { @@ -153,18 +196,21 @@ void InstanceView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bo void InstanceView::contextMenuRequested(const QPoint pos) { - QModelIndex index = m_table->indexAt(pos); + QModelIndex index = currentView()->indexAt(pos); if (index.isValid()) { int row = mappedIndex(index).row(); InstancePtr inst = m_instances->at(row); - emit showContextMenu(m_table->mapToParent(pos), inst); + emit showContextMenu(currentView()->mapToParent(pos), inst); } } QModelIndex InstanceView::mappedIndex(const QModelIndex& index) const { - return m_proxy->mapToSource(index); + if (m_displayMode == DisplayMode::GridMode) { + return m_gridProxy->mapToSource(index); + } + return m_tableProxy->mapToSource(index); } void InstanceView::setCatDisplayed(bool enabled) diff --git a/launcher/ui/instanceview/InstanceView.h b/launcher/ui/instanceview/InstanceView.h index 0822e5542..0e1903694 100644 --- a/launcher/ui/instanceview/InstanceView.h +++ b/launcher/ui/instanceview/InstanceView.h @@ -18,21 +18,32 @@ #pragma once #include +#include #include #include #include "BaseInstance.h" -class InstanceProxyModel; +class InstanceTableProxyModel; +class InstanceGridProxyModel; class InstanceList; class InstanceView : public QStackedWidget { Q_OBJECT + enum DisplayMode { TableMode = 0, GridMode }; + public: explicit InstanceView(QWidget* parent = nullptr, InstanceList* instances = nullptr); - QAbstractItemView* currentView() { return m_table; } + void switchDisplayMode(DisplayMode mode); + + QAbstractItemView* currentView() + { + if (m_displayMode == GridMode) + return m_grid; + return m_table; + } InstancePtr currentInstance(); @@ -56,12 +67,16 @@ class InstanceView : public QStackedWidget { private: void createTable(); + void createGrid(); void prepareModel(); QModelIndex mappedIndex(const QModelIndex& index) const; - int m_rowHeight = 48; + int m_iconSize = 48; + DisplayMode m_displayMode = TableMode; QTableView* m_table; - InstanceProxyModel* m_proxy; + QListView* m_grid; + InstanceTableProxyModel* m_tableProxy; + InstanceGridProxyModel* m_gridProxy; InstanceList* m_instances; };