diff --git a/rpcs3/Icons/copy_blue.png b/rpcs3/Icons/copy_blue.png new file mode 100644 index 0000000000..102e2a5567 Binary files /dev/null and b/rpcs3/Icons/copy_blue.png differ diff --git a/rpcs3/Icons/copy_gray.png b/rpcs3/Icons/copy_gray.png new file mode 100644 index 0000000000..d772a92199 Binary files /dev/null and b/rpcs3/Icons/copy_gray.png differ diff --git a/rpcs3/Icons/disc_blue.png b/rpcs3/Icons/disc_blue.png new file mode 100644 index 0000000000..ad24df1f16 Binary files /dev/null and b/rpcs3/Icons/disc_blue.png differ diff --git a/rpcs3/Icons/disc_gray.png b/rpcs3/Icons/disc_gray.png new file mode 100644 index 0000000000..d7b4158fd6 Binary files /dev/null and b/rpcs3/Icons/disc_gray.png differ diff --git a/rpcs3/Icons/grid_blue.png b/rpcs3/Icons/grid_blue.png new file mode 100644 index 0000000000..45a000ff9b Binary files /dev/null and b/rpcs3/Icons/grid_blue.png differ diff --git a/rpcs3/Icons/grid_gray.png b/rpcs3/Icons/grid_gray.png new file mode 100644 index 0000000000..ebbf6a73cf Binary files /dev/null and b/rpcs3/Icons/grid_gray.png differ diff --git a/rpcs3/Icons/harddisk_blue.png b/rpcs3/Icons/harddisk_blue.png new file mode 100644 index 0000000000..60a6d41587 Binary files /dev/null and b/rpcs3/Icons/harddisk_blue.png differ diff --git a/rpcs3/Icons/harddisk_gray.png b/rpcs3/Icons/harddisk_gray.png new file mode 100644 index 0000000000..52ec3cea1f Binary files /dev/null and b/rpcs3/Icons/harddisk_gray.png differ diff --git a/rpcs3/Icons/home_blue.png b/rpcs3/Icons/home_blue.png new file mode 100644 index 0000000000..2d36a10d7f Binary files /dev/null and b/rpcs3/Icons/home_blue.png differ diff --git a/rpcs3/Icons/home_gray.png b/rpcs3/Icons/home_gray.png new file mode 100644 index 0000000000..b4986d97e0 Binary files /dev/null and b/rpcs3/Icons/home_gray.png differ diff --git a/rpcs3/Icons/info_blue.png b/rpcs3/Icons/info_blue.png new file mode 100644 index 0000000000..f69d308fd4 Binary files /dev/null and b/rpcs3/Icons/info_blue.png differ diff --git a/rpcs3/Icons/info_gray.png b/rpcs3/Icons/info_gray.png new file mode 100644 index 0000000000..7e8afc3d48 Binary files /dev/null and b/rpcs3/Icons/info_gray.png differ diff --git a/rpcs3/Icons/list_blue.png b/rpcs3/Icons/list_blue.png new file mode 100644 index 0000000000..f0ea40b130 Binary files /dev/null and b/rpcs3/Icons/list_blue.png differ diff --git a/rpcs3/Icons/list_gray.png b/rpcs3/Icons/list_gray.png new file mode 100644 index 0000000000..7e17b4fc4f Binary files /dev/null and b/rpcs3/Icons/list_gray.png differ diff --git a/rpcs3/Icons/media_blue.png b/rpcs3/Icons/media_blue.png new file mode 100644 index 0000000000..7b64e4e970 Binary files /dev/null and b/rpcs3/Icons/media_blue.png differ diff --git a/rpcs3/Icons/media_gray.png b/rpcs3/Icons/media_gray.png new file mode 100644 index 0000000000..6ee3aed90d Binary files /dev/null and b/rpcs3/Icons/media_gray.png differ diff --git a/rpcs3/resources.qrc b/rpcs3/resources.qrc index 4ef295097d..03214a22d0 100644 --- a/rpcs3/resources.qrc +++ b/rpcs3/resources.qrc @@ -5,5 +5,21 @@ Icons/restart.png Icons/stop.png rpcs3.ico + Icons/copy_blue.png + Icons/disc_blue.png + Icons/grid_blue.png + Icons/harddisk_blue.png + Icons/home_blue.png + Icons/info_blue.png + Icons/list_blue.png + Icons/media_blue.png + Icons/copy_gray.png + Icons/disc_gray.png + Icons/grid_gray.png + Icons/harddisk_gray.png + Icons/home_gray.png + Icons/info_gray.png + Icons/list_gray.png + Icons/media_gray.png diff --git a/rpcs3/rpcs3qt.vcxproj b/rpcs3/rpcs3qt.vcxproj index cd3288ed42..8974aa2466 100644 --- a/rpcs3/rpcs3qt.vcxproj +++ b/rpcs3/rpcs3qt.vcxproj @@ -392,6 +392,11 @@ true true + + true + true + true + true true @@ -532,6 +537,11 @@ true true + + true + true + true + true true @@ -682,6 +692,11 @@ true true + + true + true + true + true true @@ -822,6 +837,11 @@ true true + + true + true + true + true true @@ -917,6 +937,8 @@ true true + + @@ -1387,6 +1409,25 @@ $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing game_list_grid.h... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(NOINHERIT)\." + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing game_list_grid.h... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(NOINHERIT)\." + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing game_list_grid.h... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(NOINHERIT)\." + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing game_list_grid.h... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(NOINHERIT)\." + + diff --git a/rpcs3/rpcs3qt.vcxproj.filters b/rpcs3/rpcs3qt.vcxproj.filters index 41f3a0f91c..ba790832e1 100644 --- a/rpcs3/rpcs3qt.vcxproj.filters +++ b/rpcs3/rpcs3qt.vcxproj.filters @@ -513,6 +513,24 @@ Generated Files\Debug - LLVM + + Gui + + + Generated Files\Release - LLVM + + + Generated Files\Debug + + + Generated Files\Release + + + Generated Files\Debug - LLVM + + + Gui + @@ -549,6 +567,9 @@ Gui + + Gui + @@ -644,5 +665,8 @@ Gui + + Gui + \ No newline at end of file diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 9a224810b4..6c1f81fce6 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -20,46 +20,134 @@ #include #include #include +#include static const std::string m_class_name = "GameViewer"; inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); } -// Auxiliary classes -class sortGameData -{ - int sortColumn; - bool sortAscending; - -public: - sortGameData(u32 column, bool ascending) : sortColumn(column), sortAscending(ascending) {} - bool operator()(const GameInfo& game1, const GameInfo& game2) const - { - // Note that the column index has to match the appropriate GameInfo member - switch (sortColumn - 1) // skip *icon* column - { - case 0: return sortAscending ? (game1.name < game2.name) : (game1.name > game2.name); - case 1: return sortAscending ? (game1.serial < game2.serial) : (game1.serial > game2.serial); - case 2: return sortAscending ? (game1.fw < game2.fw) : (game1.fw > game2.fw); - case 3: return sortAscending ? (game1.app_ver < game2.app_ver) : (game1.app_ver > game2.app_ver); - case 4: return sortAscending ? (game1.category < game2.category) : (game1.category > game2.category); - case 5: return sortAscending ? (game1.root < game2.root) : (game1.root > game2.root); - default: return false; - } - } -}; - game_list_frame::game_list_frame(std::shared_ptr settings, Render_Creator r_Creator, QWidget *parent) : QDockWidget(tr("Game List"), parent), xgui_settings(settings), m_Render_Creator(r_Creator) { - m_Icon_Size = GUI::gl_icon_size.at(m_gui_settings->GetValue(GUI::gl_iconSize).toString()); + m_isListLayout = xgui_settings->GetValue(GUI::gl_listMode).toBool(); + m_Icon_Size_Str = xgui_settings->GetValue(GUI::gl_iconSize).toString(); + m_Margin_Factor = xgui_settings->GetValue(GUI::gl_marginFactor).toReal(); + m_Text_Factor = xgui_settings->GetValue(GUI::gl_textFactor).toReal(); + m_showToolBar = xgui_settings->GetValue(GUI::gl_toolBarVisible).toBool(); - m_columns = columns_arr(m_Icon_Size); + // get icon size from list + int icon_size_index = 0; + for (int i = 0; i < GUI::gl_icon_size.count(); i++) + { + if (GUI::gl_icon_size.at(i).first == m_Icon_Size_Str) + { + m_Icon_Size = GUI::gl_icon_size.at(i).second; + icon_size_index = i; + break; + } + } - gameList = new QTableWidget(this); + // Save factors for first setup + xgui_settings->SetValue(GUI::gl_marginFactor, m_Margin_Factor); + xgui_settings->SetValue(GUI::gl_textFactor, m_Text_Factor); + xgui_settings->SetValue(GUI::gl_toolBarVisible, m_showToolBar); + + m_Game_Dock = new QMainWindow(this); + m_Game_Dock->setWindowFlags(Qt::Widget); + + // Set up toolbar + m_Tool_Bar = new QToolBar(m_Game_Dock); + m_Tool_Bar->setMovable(false); + m_Tool_Bar->setVisible(m_showToolBar); + + // ToolBar Actions + m_catActHDD = { new QAction(""), QIcon(":/Icons/harddisk_blue.png"), QIcon(":/Icons/harddisk_gray.png") }; + m_catActHDD.action->setIcon(xgui_settings->GetValue(GUI::cat_hdd_game).toBool() ? m_catActHDD.colored : m_catActHDD.gray); + m_catActHDD.action->setToolTip(tr("Show HDD Category")); + + m_catActDisc = { new QAction(""), QIcon(":/Icons/disc_blue.png"), QIcon(":/Icons/disc_gray.png") }; + m_catActDisc.action->setIcon(xgui_settings->GetValue(GUI::cat_disc_game).toBool() ? m_catActDisc.colored : m_catActDisc.gray); + m_catActDisc.action->setToolTip(tr("Show Disc Category")); + + m_catActHome = { new QAction(""), QIcon(":/Icons/home_blue.png"), QIcon(":/Icons/home_gray.png") }; + m_catActHome.action->setIcon(xgui_settings->GetValue(GUI::cat_home).toBool() ? m_catActHome.colored : m_catActHome.gray); + m_catActHome.action->setToolTip(tr("Show Home Category")); + + m_catActAudioVideo = { new QAction(""), QIcon(":/Icons/media_blue.png"), QIcon(":/Icons/media_gray.png") }; + m_catActAudioVideo.action->setIcon(xgui_settings->GetValue(GUI::cat_audio_video).toBool() ? m_catActAudioVideo.colored : m_catActAudioVideo.gray); + m_catActAudioVideo.action->setToolTip(tr("Show Audio/Video Category")); + + m_catActGameData = { new QAction(""), QIcon(":/Icons/copy_blue.png"), QIcon(":/Icons/copy_gray.png") }; + m_catActGameData.action->setIcon(xgui_settings->GetValue(GUI::cat_game_data).toBool() ? m_catActGameData.colored : m_catActGameData.gray); + m_catActGameData.action->setToolTip(tr("Show GameData Category")); + + m_catActUnknown = { new QAction(""), QIcon(":/Icons/info_blue.png"), QIcon(":/Icons/info_gray.png") }; + m_catActUnknown.action->setIcon(xgui_settings->GetValue(GUI::cat_unknown).toBool() ? m_catActUnknown.colored : m_catActUnknown.gray); + m_catActUnknown.action->setToolTip(tr("Show Unknown Category")); + + m_categoryButtons = { m_catActHDD , m_catActDisc, m_catActHome, m_catActAudioVideo, m_catActGameData, m_catActUnknown }; + + m_categoryActs = new QActionGroup(m_Tool_Bar); + m_categoryActs->addAction(m_catActHDD.action); + m_categoryActs->addAction(m_catActDisc.action); + m_categoryActs->addAction(m_catActHome.action); + m_categoryActs->addAction(m_catActAudioVideo.action); + m_categoryActs->addAction(m_catActGameData.action); + m_categoryActs->addAction(m_catActUnknown.action); + m_categoryActs->setEnabled(m_isListLayout); + + m_modeActList = { new QAction(""), QIcon(":/Icons/list_blue.png"), QIcon(":/Icons/list_gray.png") }; + m_modeActList.action->setIcon(m_isListLayout ? m_modeActList.colored : m_modeActList.gray); + m_modeActList.action->setToolTip(tr("Enable List Mode")); + + m_modeActGrid = { new QAction(""), QIcon(":/Icons/grid_blue.png"), QIcon(":/Icons/grid_gray.png") }; + m_modeActGrid.action->setIcon(m_isListLayout ? m_modeActGrid.gray : m_modeActGrid.colored); + m_modeActGrid.action->setToolTip(tr("Enable Grid Mode")); + + m_modeActs = new QActionGroup(m_Tool_Bar); + m_modeActs->addAction(m_modeActList.action); + m_modeActs->addAction(m_modeActGrid.action); + + // Search Bar + m_Search_Bar = new QLineEdit(m_Tool_Bar); + m_Search_Bar->setPlaceholderText(tr("Search games ...")); + connect(m_Search_Bar, &QLineEdit::textChanged, [this]() { + Refresh(); + }); + + // Icon Size Slider + m_Slider_Size = new QSlider(Qt::Horizontal , m_Tool_Bar); + m_Slider_Size->setRange(0, GUI::gl_icon_size.size() - 1); + m_Slider_Size->setSliderPosition(icon_size_index); + m_Slider_Size->setFixedWidth(100); + + m_Tool_Bar->addWidget(m_Search_Bar); + m_Tool_Bar->addWidget(new QLabel(" ")); + m_Tool_Bar->addSeparator(); + m_Tool_Bar->addWidget(new QLabel(" ")); + m_Tool_Bar->addActions(m_categoryActs->actions()); + m_Tool_Bar->addWidget(new QLabel(" ")); + m_Tool_Bar->addSeparator(); + m_Tool_Bar->addWidget(new QLabel(tr(" View Mode "))); + m_Tool_Bar->addAction(m_modeActList.action); + m_Tool_Bar->addAction(m_modeActGrid.action); + m_Tool_Bar->addWidget(new QLabel(tr(" "))); + m_Tool_Bar->addSeparator(); + m_Tool_Bar->addWidget(new QLabel(tr(" Tiny "))); // Can this be any easier? + m_Tool_Bar->addWidget(m_Slider_Size); + m_Tool_Bar->addWidget(new QLabel(tr(" Large "))); + + m_Game_Dock->addToolBar(m_Tool_Bar); + setWidget(m_Game_Dock); + + bool showText = (m_Icon_Size_Str != GUI::gl_icon_key_small && m_Icon_Size_Str != GUI::gl_icon_key_tiny); + m_xgrid.reset(new game_list_grid(m_Icon_Size, m_Margin_Factor, m_Text_Factor, showText)); + + gameList = new QTableWidget(); gameList->setShowGrid(false); gameList->setItemDelegate(new table_item_delegate(this)); gameList->setSelectionBehavior(QAbstractItemView::SelectRows); gameList->setSelectionMode(QAbstractItemView::SingleSelection); + gameList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); gameList->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); gameList->verticalHeader()->setMinimumSectionSize(m_Icon_Size.height()); gameList->verticalHeader()->setMaximumSectionSize(m_Icon_Size.height()); @@ -67,7 +155,7 @@ game_list_frame::game_list_frame(std::shared_ptr settings, Render_ gameList->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); gameList->setContextMenuPolicy(Qt::CustomContextMenu); - gameList->setColumnCount(7); + gameList->setColumnCount(8); gameList->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("Icon"))); gameList->setHorizontalHeaderItem(1, new QTableWidgetItem(tr("Name"))); gameList->setHorizontalHeaderItem(2, new QTableWidgetItem(tr("Serial"))); @@ -75,8 +163,16 @@ game_list_frame::game_list_frame(std::shared_ptr settings, Render_ gameList->setHorizontalHeaderItem(4, new QTableWidgetItem(tr("App version"))); gameList->setHorizontalHeaderItem(5, new QTableWidgetItem(tr("Category"))); gameList->setHorizontalHeaderItem(6, new QTableWidgetItem(tr("Path"))); + gameList->setHorizontalHeaderItem(7, new QTableWidgetItem(tr("Missingno"))); // Holds index which points back to original array - setWidget(gameList); + gameList->setColumnHidden(7, true); // Comment this if your sorting ever for whatever reason messes up. + + m_Central_Widget = new QStackedWidget(this); + m_Central_Widget->addWidget(gameList); + m_Central_Widget->addWidget(m_xgrid.get()); + m_Central_Widget->setCurrentWidget(m_isListLayout ? gameList : m_xgrid.get()); + + m_Game_Dock->setCentralWidget(m_Central_Widget); // Actions showIconColAct = new QAction(tr("Show Icons"), this); @@ -99,6 +195,21 @@ game_list_frame::game_list_frame(std::shared_ptr settings, Render_ connect(gameList, &QTableWidget::doubleClicked, this, &game_list_frame::doubleClickedSlot); connect(gameList->horizontalHeader(), &QHeaderView::sectionClicked, this, &game_list_frame::OnColClicked); + connect(m_xgrid.get(), &QTableWidget::doubleClicked, this, &game_list_frame::doubleClickedSlot); + connect(m_xgrid.get(), &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu); + + connect(m_Slider_Size, &QSlider::valueChanged, [=](int value) { emit RequestIconSizeActSet(value); }); + + connect(m_modeActs, &QActionGroup::triggered, [=](QAction* act) { + emit RequestListModeActSet(act == m_modeActList.action); + m_modeActList.action->setIcon(m_isListLayout ? m_modeActList.colored : m_modeActList.gray); + m_modeActGrid.action->setIcon(m_isListLayout ? m_modeActGrid.gray : m_modeActGrid.colored); + }); + + connect(m_categoryActs, &QActionGroup::triggered, [=](QAction* act) { + emit RequestCategoryActSet(m_categoryActs->actions().indexOf(act)); + }); + for (int col = 0; col < columnActs.count(); ++col) { columnActs[col]->setCheckable(true); @@ -120,11 +231,11 @@ game_list_frame::game_list_frame(std::shared_ptr settings, Render_ gameList->setColumnHidden(col, !val); // Negate because it's a set col hidden and we have menu say show. xgui_settings->SetGamelistColVisibility(col, val); }; + columnActs[col]->setChecked(xgui_settings->GetGamelistColVisibility(col)); connect(columnActs[col], &QAction::triggered, l_CallBack); } // Init - Refresh(); // Data MUST be loaded so that first settings load will reset columns to correct width w/r to data. LoadSettings(); } @@ -138,7 +249,9 @@ void game_list_frame::LoadSettings() columnActs[col]->setChecked(vis); gameList->setColumnHidden(col, !vis); } - m_sortAscending = xgui_settings->GetValue(GUI::gl_sortAsc).toBool(); + bool sortAsc = Qt::SortOrder(xgui_settings->GetValue(GUI::gl_sortAsc).toBool()); + m_colSortOrder = sortAsc ? Qt::AscendingOrder : Qt::DescendingOrder; + m_sortColumn = xgui_settings->GetValue(GUI::gl_sortCol).toInt(); m_categoryFilters = xgui_settings->GetGameListCategoryFilters(); @@ -154,7 +267,7 @@ void game_list_frame::LoadSettings() gameList->horizontalHeader()->restoreState(state); } - Refresh(); + Refresh(true); } game_list_frame::~game_list_frame() @@ -164,109 +277,22 @@ game_list_frame::~game_list_frame() void game_list_frame::OnColClicked(int col) { + if (col == 0) return; // Don't "sort" icons. + if (col == m_sortColumn) { - m_sortAscending ^= true; + m_colSortOrder = (m_colSortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder; } else { - m_sortAscending = true; + m_colSortOrder = Qt::AscendingOrder; } m_sortColumn = col; - xgui_settings->SetValue(GUI::gl_sortAsc, m_sortAscending); + xgui_settings->SetValue(GUI::gl_sortAsc, m_colSortOrder == Qt::AscendingOrder); xgui_settings->SetValue(GUI::gl_sortCol, col); - // Sort entries, update columns and refresh the panel - Refresh(); -} - - -void game_list_frame::LoadGames() -{ - m_games.clear(); - - for (const auto& entry : fs::dir(Emu.GetGameDir())) - { - if (entry.is_directory) - { - m_games.push_back(entry.name); - } - } -} - -void game_list_frame::LoadPSF() -{ - m_game_data.clear(); - - const std::string& game_path = Emu.GetGameDir(); - - for (u32 i = 0; i < m_games.size(); ++i) - { - const std::string& dir = game_path + m_games[i]; - const std::string& sfb = dir + "/PS3_DISC.SFB"; - const std::string& sfo = dir + (fs::is_file(sfb) ? "/PS3_GAME/PARAM.SFO" : "/PARAM.SFO"); - - const fs::file sfo_file(sfo); - if (!sfo_file) - { - continue; - } - - const auto& psf = psf::load_object(sfo_file); - - GameInfo game; - game.root = m_games[i]; - game.serial = psf::get_string(psf, "TITLE_ID", ""); - game.name = psf::get_string(psf, "TITLE", "unknown"); - game.app_ver = psf::get_string(psf, "APP_VER", "unknown"); - game.category = psf::get_string(psf, "CATEGORY", "unknown"); - game.fw = psf::get_string(psf, "PS3_SYSTEM_VER", "unknown"); - game.parental_lvl = psf::get_integer(psf, "PARENTAL_LEVEL"); - game.resolution = psf::get_integer(psf, "RESOLUTION"); - game.sound_format = psf::get_integer(psf, "SOUND_FORMAT"); - - if (game.category == "HG") - { - game.category = sstr(category::hdd_Game); - game.icon_path = dir + "/ICON0.PNG"; - } - else if (game.category == "DG") - { - game.category = sstr(category::disc_Game); - game.icon_path = dir + "/PS3_GAME/ICON0.PNG"; - } - else if (game.category == "HM") - { - game.category = sstr(category::home); - game.icon_path = dir + "/ICON0.PNG"; - } - else if (game.category == "AV") - { - game.category = sstr(category::audio_Video); - game.icon_path = dir + "/ICON0.PNG"; - } - else if (game.category == "GD") - { - game.category = sstr(category::game_Data); - game.icon_path = dir + "/ICON0.PNG"; - } - else if (game.category == "unknown") - { - game.category = sstr(category::unknown); - } - - m_game_data.push_back(game); - } - - // Sort entries and update columns - std::sort(m_game_data.begin(), m_game_data.end(), sortGameData(m_sortColumn, m_sortAscending)); - m_columns.Update(m_game_data); -} - -void game_list_frame::ShowData() -{ - m_columns.ShowData(gameList); + gameList->sortByColumn(m_sortColumn, m_colSortOrder); } // Filter for Categories @@ -291,14 +317,138 @@ void game_list_frame::FilterData() } } -void game_list_frame::Refresh() +void game_list_frame::Refresh(bool fromDrive) { - int row = gameList->currentRow(); - LoadGames(); - LoadPSF(); - ShowData(); - FilterData(); - gameList->selectRow(row); + if (fromDrive) + { + // Load PSF + + m_game_data.clear(); + + const std::string& game_path = Emu.GetGameDir(); + + for (const auto& entry : fs::dir(Emu.GetGameDir())) + { + if (!entry.is_directory) + { + continue; + } + + const std::string& dir = game_path + entry.name; + const std::string& sfb = dir + "/PS3_DISC.SFB"; + const std::string& sfo = dir + (fs::is_file(sfb) ? "/PS3_GAME/PARAM.SFO" : "/PARAM.SFO"); + + const fs::file sfo_file(sfo); + if (!sfo_file) + { + continue; + } + + const auto& psf = psf::load_object(sfo_file); + + GameInfo game; + game.root = entry.name; + game.serial = psf::get_string(psf, "TITLE_ID", ""); + game.name = psf::get_string(psf, "TITLE", "unknown"); + game.app_ver = psf::get_string(psf, "APP_VER", "unknown"); + game.category = psf::get_string(psf, "CATEGORY", "unknown"); + game.fw = psf::get_string(psf, "PS3_SYSTEM_VER", "unknown"); + game.parental_lvl = psf::get_integer(psf, "PARENTAL_LEVEL"); + game.resolution = psf::get_integer(psf, "RESOLUTION"); + game.sound_format = psf::get_integer(psf, "SOUND_FORMAT"); + + if (game.category == "HG") + { + game.category = sstr(category::hdd_Game); + game.icon_path = dir + "/ICON0.PNG"; + } + else if (game.category == "DG") + { + game.category = sstr(category::disc_Game); + game.icon_path = dir + "/PS3_GAME/ICON0.PNG"; + } + else if (game.category == "HM") + { + game.category = sstr(category::home); + game.icon_path = dir + "/ICON0.PNG"; + } + else if (game.category == "AV") + { + game.category = sstr(category::audio_Video); + game.icon_path = dir + "/ICON0.PNG"; + } + else if (game.category == "GD") + { + game.category = sstr(category::game_Data); + game.icon_path = dir + "/ICON0.PNG"; + } + else if (game.category == "unknown") + { + game.category = sstr(category::unknown); + } + + // Load Image + QImage img; + QPixmap pxmap; + + if (!game.icon_path.empty() && img.load(qstr(game.icon_path))) + { + QImage scaled = img.scaled(m_Icon_Size, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation); + pxmap = QPixmap::fromImage(scaled); + } + else + { + img = QImage(m_Icon_Size, QImage::Format_ARGB32); + QString abspath = QDir(qstr(game.icon_path)).absolutePath(); + LOG_ERROR(HLE, "Count not load image from path %s", sstr(abspath)); + img.fill(QColor(0, 0, 0, 0)); + pxmap = QPixmap::fromImage(img); + } + + m_game_data.push_back({ game, img, pxmap }); + } + + auto op = [](const GUI_GameInfo& game1, const GUI_GameInfo& game2) { + return game1.info.name < game2.info.name; + }; + + // Sort by name at the very least. + std::sort(m_game_data.begin(), m_game_data.end(), op); + } + + // Fill Game List / Game Grid + + if (m_isListLayout) + { + int row = gameList->currentRow(); + + PopulateGameList(); + FilterData(); + gameList->selectRow(row); + gameList->sortByColumn(m_sortColumn, m_colSortOrder); + gameList->setColumnHidden(7, true); + gameList->verticalHeader()->setMinimumSectionSize(m_Icon_Size.height()); + gameList->verticalHeader()->setMaximumSectionSize(m_Icon_Size.height()); + gameList->resizeRowsToContents(); + gameList->resizeColumnToContents(0); + } + else + { + if (m_Icon_Size.width() > 0 && m_Icon_Size.height() > 0) + { + m_games_per_row = width() / (m_Icon_Size.width() + m_Icon_Size.width() * m_xgrid.get()->getMarginFactor() * 2); + } + else + { + m_games_per_row = 0; + } + + m_xgrid.reset(MakeGrid(m_games_per_row, m_Icon_Size)); + connect(m_xgrid.get(), &QTableWidget::doubleClicked, this, &game_list_frame::doubleClickedSlot); + connect(m_xgrid.get(), &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu); + m_Central_Widget->addWidget(m_xgrid.get()); + m_Central_Widget->setCurrentWidget(m_xgrid.get()); + } } void game_list_frame::ToggleCategoryFilter(QString category, bool show) @@ -314,7 +464,7 @@ void game_list_frame::SaveSettings() xgui_settings->SetGamelistColVisibility(col, columnActs[col]->isChecked()); } xgui_settings->SetValue(GUI::gl_sortCol, m_sortColumn); - xgui_settings->SetValue(GUI::gl_sortAsc, m_sortAscending); + xgui_settings->SetValue(GUI::gl_sortAsc, m_colSortOrder == Qt::AscendingOrder); xgui_settings->SetValue(GUI::gl_state, gameList->horizontalHeader()->saveState()); } @@ -338,46 +488,83 @@ static void open_dir(const std::string& spath) void game_list_frame::doubleClickedSlot(const QModelIndex& index) { - int i = index.row(); - QString category = qstr(m_game_data[i].category); + int i; + if (m_isListLayout) + { + i = gameList->item(index.row(), 7)->text().toInt(); + } + else + { + i = m_xgrid->item(index.row(), index.column())->data(Qt::ItemDataRole::UserRole).toInt(); + } + + QString category = qstr(m_game_data[i].info.category); + // Boot these categories if (category == category::hdd_Game || category == category::disc_Game || category == category::audio_Video) { - const std::string& path = Emu.GetGameDir() + m_game_data[i].root; + const std::string& path = Emu.GetGameDir() + m_game_data[i].info.root; emit RequestIconPathSet(path); - + Emu.Stop(); - + if (!Emu.BootGame(path)) { - LOG_ERROR(LOADER, "Failed to boot /dev_hdd0/game/%s", m_game_data[i].root); + LOG_ERROR(LOADER, "Failed to boot /dev_hdd0/game/%s", m_game_data[i].info.root); } else { LOG_SUCCESS(LOADER, "Boot from gamelist per doubleclick: done"); - emit RequestAddRecentGame(q_string_pair(qstr(path), qstr("[" + m_game_data[i].serial + "] " + m_game_data[i].name))); + emit RequestAddRecentGame(q_string_pair(qstr(path), qstr("[" + m_game_data[i].info.serial + "] " + m_game_data[i].info.name))); } } else { - open_dir(Emu.GetGameDir() + m_game_data[i].root); + open_dir(Emu.GetGameDir() + m_game_data[i].info.root); } } -void game_list_frame::ShowContextMenu(const QPoint &pos) // this is a slot +void game_list_frame::ShowContextMenu(const QPoint &pos) +{ + int index; + + if (m_isListLayout) + { + int row = gameList->indexAt(pos).row(); + QTableWidgetItem* item = gameList->item(row, 7); + if (item == nullptr) return; // null happens if you are double clicking in dockwidget area on nothing. + index = item->text().toInt(); + } + else + { + int row = m_xgrid->indexAt(pos).row(); + int col = m_xgrid->indexAt(pos).column(); + QTableWidgetItem* item = m_xgrid->item(row, col); + if (item == nullptr) return; // null happens if you are double clicking in dockwidget area on nothing. + index = item->data(Qt::ItemDataRole::UserRole).toInt(); + if (index == -1) return; // empty item shouldn't have context menu + } + ShowSpecifiedContextMenu(pos, index); +} + +void game_list_frame::ShowSpecifiedContextMenu(const QPoint &pos, int row) { - int row = gameList->indexAt(pos).row(); - if (row == -1) { return; // invalid } - // for most widgets - QPoint globalPos = gameList->mapToGlobal(pos); - // for QAbstractScrollArea and derived classes you would use: - // QPoint globalPos = myWidget->viewport()->mapToGlobal(pos); + QPoint globalPos; + + if (m_isListLayout) + { + globalPos = gameList->mapToGlobal(pos); + } + else + { + globalPos = m_xgrid->mapToGlobal(pos); + } QMenu myMenu; @@ -397,25 +584,28 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) // this is a slot QAction* checkCompat = myMenu.addAction(tr("&Check Game Compatibility")); connect(boot, &QAction::triggered, [=]() {Boot(row); }); - connect(configure, &QAction::triggered, [=](){ - settings_dialog(xgui_settings, m_Render_Creator, this, &m_game_data[row]).exec(); + connect(configure, &QAction::triggered, [=]() { + settings_dialog(xgui_settings, m_Render_Creator, this, &m_game_data[row].info).exec(); }); - connect(removeGame, &QAction::triggered, [=](){ + connect(removeGame, &QAction::triggered, [=]() { if (QMessageBox::question(this, tr("Confirm Delete"), tr("Permanently delete files?")) == QMessageBox::Yes) - fs::remove_all(Emu.GetGameDir() + m_game_data[row].root); - Refresh(); + { + fs::remove_all(Emu.GetGameDir() + m_game_data[row].info.root); + m_game_data.erase(m_game_data.begin() + row); + Refresh(); + } }); connect(removeConfig, &QAction::triggered, [=]() {RemoveCustomConfiguration(row); }); - connect(openGameFolder, &QAction::triggered, [=]() {open_dir(Emu.GetGameDir() + m_game_data[row].root); }); - connect(openConfig, &QAction::triggered, [=]() {open_dir(fs::get_config_dir() + "data/" + m_game_data[row].serial); }); + connect(openGameFolder, &QAction::triggered, [=]() {open_dir(Emu.GetGameDir() + m_game_data[row].info.root); }); + connect(openConfig, &QAction::triggered, [=]() {open_dir(fs::get_config_dir() + "data/" + m_game_data[row].info.serial); }); connect(checkCompat, &QAction::triggered, [=]() { - QString serial = qstr(m_game_data[row].serial); + QString serial = qstr(m_game_data[row].info.serial); QString link = "https://rpcs3.net/compatibility?g=" + serial; QDesktopServices::openUrl(QUrl(link)); }); //Disable options depending on software category - QString category = qstr(m_game_data[row].category); + QString category = qstr(m_game_data[row].info.category); if (category == category::disc_Game) { @@ -436,32 +626,32 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) // this is a slot openConfig->setEnabled(false); checkCompat->setEnabled(false); } - + myMenu.exec(globalPos); } void game_list_frame::Boot(int row) { - const std::string& path = Emu.GetGameDir() + m_game_data[row].root; + const std::string& path = Emu.GetGameDir() + m_game_data[row].info.root; emit RequestIconPathSet(path); Emu.Stop(); if (!Emu.BootGame(path)) { - QMessageBox::warning(this, tr("Warning!"), tr("Failed to boot ") + qstr(m_game_data[row].root)); - LOG_ERROR(LOADER, "Failed to boot /dev_hdd0/game/%s", m_game_data[row].root); + QMessageBox::warning(this, tr("Warning!"), tr("Failed to boot ") + qstr(m_game_data[row].info.root)); + LOG_ERROR(LOADER, "Failed to boot /dev_hdd0/game/%s", m_game_data[row].info.root); } else { LOG_SUCCESS(LOADER, "Boot from gamelist per Boot: done"); - emit RequestAddRecentGame(q_string_pair(qstr(path), qstr("[" + m_game_data[row].serial + "] " + m_game_data[row].name))); + emit RequestAddRecentGame(q_string_pair(qstr(path), qstr("[" + m_game_data[row].info.serial + "] " + m_game_data[row].info.name))); } } void game_list_frame::RemoveCustomConfiguration(int row) { - const std::string config_path = fs::get_config_dir() + "data/" + m_game_data[row].serial + "/config.yml"; + const std::string config_path = fs::get_config_dir() + "data/" + m_game_data[row].info.serial + "/config.yml"; if (fs::is_file(config_path)) { @@ -485,164 +675,48 @@ void game_list_frame::RemoveCustomConfiguration(int row) } } -void game_list_frame::ResizeIcons(QSize size) +void game_list_frame::ResizeIcons(const QSize& size, const int& idx) { - m_columns.m_Icon_Size = size; + m_Slider_Size->setSliderPosition(idx); + m_Icon_Size_Str = GUI::gl_icon_size.at(idx).first; + + xgui_settings->SetValue(GUI::gl_iconSize, m_Icon_Size_Str); + m_Icon_Size = size; - gameList->verticalHeader()->setMinimumSectionSize(m_Icon_Size.height()); - gameList->verticalHeader()->setMaximumSectionSize(m_Icon_Size.height()); + + for (size_t i = 0; i < m_game_data.size(); i++) + { + m_game_data[i].pxmap = QPixmap::fromImage(m_game_data[i].icon.scaled(m_Icon_Size, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation)); + } + Refresh(); } -columns_arr::columns_arr(QSize icon_Size) : m_Icon_Size(icon_Size) +void game_list_frame::SetListMode(const bool& isList) { - m_img_list = new QList(); + m_isListLayout = isList; - m_columns.clear(); - m_columns.emplace_back(0, 90, "Icon"); - m_columns.emplace_back(1, 160, "Name"); - m_columns.emplace_back(2, 85, "Serial"); - m_columns.emplace_back(3, 55, "FW"); - m_columns.emplace_back(4, 55, "App version"); - m_columns.emplace_back(5, 75, "Category"); - m_columns.emplace_back(6, 160, "Path"); - m_col_icon = &m_columns[0]; - m_col_name = &m_columns[1]; - m_col_serial = &m_columns[2]; - m_col_fw = &m_columns[3]; - m_col_app_ver = &m_columns[4]; - m_col_category = &m_columns[5]; - m_col_path = &m_columns[6]; + xgui_settings->SetValue(GUI::gl_listMode, isList); + + m_categoryActs->setEnabled(isList); + m_modeActList.action->setIcon(m_isListLayout ? m_modeActList.colored : m_modeActList.gray); + m_modeActGrid.action->setIcon(m_isListLayout ? m_modeActGrid.gray : m_modeActGrid.colored); + + Refresh(); + + m_Central_Widget->setCurrentWidget(m_isListLayout ? gameList : m_xgrid.get()); } -Column* columns_arr::GetColumnByPos(u32 pos) +void game_list_frame::SetToolBarVisible(const bool& showToolBar) { - std::vector columns; - for (u32 pos = 0; posshown) - { - pos++; - continue; - } - if (columns[c]->pos != pos) continue; - return columns[c]; - } - - return NULL; + m_showToolBar = showToolBar; + m_Tool_Bar->setVisible(showToolBar); + xgui_settings->SetValue(GUI::gl_toolBarVisible, showToolBar); } -void columns_arr::Update(const std::vector& game_data) +void game_list_frame::SetCategoryActIcon(const int& id, const bool& active) { - m_img_list->clear(); - m_col_icon->data.clear(); - m_col_name->data.clear(); - m_col_serial->data.clear(); - m_col_fw->data.clear(); - m_col_app_ver->data.clear(); - m_col_category->data.clear(); - m_col_path->data.clear(); - m_icon_indexes.clear(); - - if (m_columns.size() == 0) return; - - for (const auto& game : game_data) - { - m_col_icon->data.push_back(game.icon_path); - m_col_name->data.push_back(game.name); - m_col_serial->data.push_back(game.serial); - m_col_fw->data.push_back(game.fw); - m_col_app_ver->data.push_back(game.app_ver); - m_col_category->data.push_back(game.category); - m_col_path->data.push_back(game.root); - } - - int c = 0; - // load icons - for (const auto& path : m_col_icon->data) - { - QImage* img = new QImage(m_Icon_Size, QImage::Format_ARGB32); - if (!path.empty()) - { - // Load image. - bool success = img->load(qstr(path)); - if (success) - { - m_img_list->append(new QImage(img->scaled(m_Icon_Size, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation))); - } - else { - // IIRC a load failure means blank image which is fine to have as a placeholder. - QString abspath = QDir(qstr(path)).absolutePath(); - LOG_ERROR(HLE, "Could not load game icon image from path %s", sstr(abspath)); - img->fill(QColor(0, 0, 0, 0)); - m_img_list->append(img); - } - } - else - { - LOG_ERROR(HLE, "Could not load game icon image from empty path"); - img->fill(QColor(0, 0, 0, 0)); - m_img_list->append(img); - } - m_icon_indexes.push_back(c); - c++; - } -} - -void columns_arr::ShowData(QTableWidget* table) -{ - // Hack to delete everything without removing the headers. - table->setRowCount(0); - - // Expect number of columns to be the same as number of icons. - table->setRowCount(m_img_list->length()); - - // Add icons. - for (int r = 0; r < m_img_list->length(); ++r) - { - QTableWidgetItem* iconItem = new QTableWidgetItem; - iconItem->setFlags(iconItem->flags() & ~Qt::ItemIsEditable); - iconItem->setData(Qt::DecorationRole, QPixmap::fromImage(*m_img_list->at(m_icon_indexes[r]))); - table->setItem(r, 0, iconItem); - } - - // Add the other data. - for (int c = 1; c < table->columnCount(); ++c) - { - Column* col = GetColumnByPos(c); - - if (!col) - { - LOG_ERROR(HLE, "Columns loaded with error!"); - return; - } - - int numRows = col->data.size(); - if (numRows != table->rowCount()) - { - table->setRowCount(numRows); - LOG_WARNING(HLE, "Warning. Columns are of different size: number of icons %d number wanted: %d", m_img_list->length(), numRows); - } - - for (int r = 0; rdata.size(); ++r) - { - QTableWidgetItem* curr = new QTableWidgetItem; - curr->setFlags(curr->flags() & ~Qt::ItemIsEditable); - QString text = qstr(col->data[r]); - curr->setText(text); - table->setItem(r, c, curr); - } - table->resizeRowsToContents(); - table->resizeColumnToContents(0); - } + m_categoryButtons.at(id).action->setIcon(active ? m_categoryButtons.at(id).colored : m_categoryButtons.at(id).gray); } void game_list_frame::closeEvent(QCloseEvent *event) @@ -650,3 +724,166 @@ void game_list_frame::closeEvent(QCloseEvent *event) QDockWidget::closeEvent(event); emit game_list_frameClosed(); } + +void game_list_frame::resizeEvent(QResizeEvent *event) +{ + if (!m_isListLayout) + { + Refresh(); + } + QDockWidget::resizeEvent(event); +} + +/** + Cleans and readds entries to table widget in UI. +*/ +void game_list_frame::PopulateGameList() +{ + // Hack to delete everything without removing the headers. + gameList->setRowCount(0); + + gameList->setRowCount(m_game_data.size()); + + auto l_GetItem = [](const std::string& text) + { + QTableWidgetItem* curr = new QTableWidgetItem; + curr->setFlags(curr->flags() & ~Qt::ItemIsEditable); + QString qtext = qstr(text); + curr->setText(qtext); + return curr; + }; + + int row = 0; + for (GUI_GameInfo game : m_game_data) + { + if (SearchMatchesApp(game.info.name, game.info.serial) == false) + { + // We aren't showing this entry. Decrement row count to avoid empty entries at end. + gameList->setRowCount(gameList->rowCount() - 1); + continue; + } + + // Icon + QTableWidgetItem* iconItem = new QTableWidgetItem; + iconItem->setFlags(iconItem->flags() & ~Qt::ItemIsEditable); + iconItem->setData(Qt::DecorationRole, game.pxmap); + gameList->setItem(row, 0, iconItem); + + gameList->setItem(row, 1, l_GetItem(game.info.name)); + gameList->setItem(row, 2, l_GetItem(game.info.serial)); + gameList->setItem(row, 3, l_GetItem(game.info.fw)); + gameList->setItem(row, 4, l_GetItem(game.info.app_ver)); + gameList->setItem(row, 5, l_GetItem(game.info.category)); + gameList->setItem(row, 6, l_GetItem(game.info.root)); + + // A certain magical index which points back to the original game index. + // Essentially, this column makes the tablewidget's row into a map, accomplishing what columns did but much simpler. + QTableWidgetItem* index = new QTableWidgetItem; + index->setText(QString::number(row)); + gameList->setItem(row, 7, index); + + row++; + } +} + +game_list_grid* game_list_frame::MakeGrid(uint maxCols, const QSize& image_size) +{ + uint r = 0; + uint c = 0; + + game_list_grid* grid; + + bool showText = m_Icon_Size_Str != GUI::gl_icon_key_small && m_Icon_Size_Str != GUI::gl_icon_key_tiny; + + if (m_Icon_Size_Str == GUI::gl_icon_key_medium) + { + grid = new game_list_grid(image_size, m_Margin_Factor, m_Text_Factor * 2, showText); + } + else + { + grid = new game_list_grid(image_size, m_Margin_Factor, m_Text_Factor, showText); + } + + // Get number of things that'll be in grid and precompute grid size. + int entries = 0; + for (GUI_GameInfo game : m_game_data) + { + if (qstr(game.info.category) == category::disc_Game || qstr(game.info.category) == category::hdd_Game) + { + if (SearchMatchesApp(game.info.name, game.info.serial) == false) + { + continue; + } + ++entries; + } + } + + // Edge cases! + if (entries == 0) + { // For whatever reason, 0%x is division by zero. Absolute nonsense by definition of modulus. But, I'll acquiesce. + return grid; + } + if (maxCols == 0) + { + maxCols = 1; + } + if (maxCols > entries) + { + maxCols = entries; + } + + int needsExtraRow = (entries % maxCols) != 0; + int maxRows = needsExtraRow + entries / maxCols; + grid->setRowCount(maxRows); + grid->setColumnCount(maxCols); + + for (uint i = 0; i < m_game_data.size(); i++) + { + if (SearchMatchesApp(m_game_data[i].info.name, m_game_data[i].info.serial) == false) + { + continue; + } + + QString category = qstr(m_game_data[i].info.category); + + if (category == category::hdd_Game || category == category::disc_Game) + { + grid->addItem(m_game_data[i].pxmap, qstr(m_game_data[i].info.name), i, r, c); + + if (++c >= maxCols) + { + c = 0; + r++; + } + } + } + + if (c != 0) + { // if left over games exist -- if empty entries exist + for (int col = c; col < maxCols; ++col) + { + QTableWidgetItem* emptyItem = new QTableWidgetItem(); + emptyItem->setFlags(Qt::NoItemFlags); + emptyItem->setData(Qt::UserRole, -1); + grid->setItem(r, col, emptyItem); + } + } + + grid->resizeColumnsToContents(); + grid->resizeRowsToContents(); + + return grid; +} + +/** +* Returns false if the game should be hidden because it doesn't match search term in toolbar. +*/ +bool game_list_frame::SearchMatchesApp(const std::string& name, const std::string& serial) +{ + if (m_Search_Bar->text() != "") + { + QString searchText = m_Search_Bar->text().toLower(); + return qstr(name).toLower().contains(searchText) || qstr(serial).toLower().contains(searchText); + } + return true; +} diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index 6ae893fde7..f7feaf893c 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -4,63 +4,19 @@ #include "stdafx.h" #include "Emu/GameInfo.h" +#include "game_list_grid.h" #include "gui_settings.h" #include "emu_settings.h" #include #include #include +#include +#include +#include +#include -struct Column -{ - u32 pos; - u32 width; - bool shown; - std::vector data; - - const std::string name; - const u32 def_pos; - const u32 def_width; - - Column(const u32 _def_pos, const u32 _def_width, const std::string& _name) - : def_pos(_def_pos) - , def_width(_def_width) - , pos(_def_pos) - , width(_def_width) - , shown(true) - , name(_name) - { - data.clear(); - } - -}; - -struct columns_arr -{ - std::vector m_columns; - - columns_arr(){}; - columns_arr(QSize icon_Size); - - Column* GetColumnByPos(u32 pos); - -public: - Column* m_col_icon; - Column* m_col_name; - Column* m_col_serial; - Column* m_col_fw; - Column* m_col_app_ver; - Column* m_col_category; - Column* m_col_path; - - QSize m_Icon_Size; - QList* m_img_list; - std::vector m_icon_indexes; - - void Update(const std::vector& game_data); - - void ShowData(QTableWidget* list); -}; +#include namespace category { @@ -72,23 +28,28 @@ namespace category const QString unknown = QObject::tr("Unknown"); } +/* Having the icons associated with the game info simplifies logic internally */ +typedef struct GUI_GameInfo +{ + GameInfo info; + QImage icon; + QPixmap pxmap; +}; + +typedef struct Tool_Bar_Button +{ + QAction* action; + QIcon colored; + QIcon gray; +}; + class game_list_frame : public QDockWidget { Q_OBJECT - int m_sortColumn; - bool m_sortAscending; - std::vector m_games; - std::vector m_game_data; - gui_settings* m_gui_settings = new gui_settings(this); - QSize m_Icon_Size; - columns_arr m_columns; - QStringList m_categoryFilters; - Render_Creator m_Render_Creator; - public: explicit game_list_frame(std::shared_ptr settings, Render_Creator r_Creator, QWidget *parent = nullptr); ~game_list_frame(); - void Refresh(); + void Refresh(const bool fromDrive = false); void ToggleCategoryFilter(QString category, bool show); /** Loads from settings. Public so that main frame can easily reset these settings if needed. */ @@ -99,31 +60,46 @@ public: public slots: /** Resize Gamelist Icons to size */ - void ResizeIcons(QSize size); + void ResizeIcons(const QSize& size, const int& idx); + void SetListMode(const bool& isList); + void SetToolBarVisible(const bool& showToolBar); + void SetCategoryActIcon(const int& id, const bool& active); private slots: void Boot(int row); void RemoveCustomConfiguration(int row); - void OnColClicked(int col); + + void ShowContextMenu(const QPoint &pos); + void ShowSpecifiedContextMenu(const QPoint &pos, int index); // Different name because the notation for overloaded connects is messy + void doubleClickedSlot(const QModelIndex& index); signals: void game_list_frameClosed(); void RequestIconPathSet(const std::string path); void RequestAddRecentGame(const q_string_pair& entry); + void RequestIconSizeActSet(const int& idx); + void RequestListModeActSet(const bool& isList); + void RequestCategoryActSet(const int& id); protected: /** Override inherited method from Qt to allow signalling when close happened.*/ void closeEvent(QCloseEvent* event); + void resizeEvent(QResizeEvent *event); private: - QTableWidget *gameList; - - void ShowContextMenu(const QPoint &pos); - void doubleClickedSlot(const QModelIndex& index); - - void LoadGames(); - void LoadPSF(); - void ShowData(); + game_list_grid* MakeGrid(uint maxCols, const QSize& image_size); void FilterData(); + void PopulateGameList(); + bool SearchMatchesApp(const std::string& name, const std::string& serial); + + // Which widget we are displaying depends on if we are in grid or list mode. + QMainWindow* m_Game_Dock; + QStackedWidget* m_Central_Widget; + QToolBar* m_Tool_Bar; + QLineEdit* m_Search_Bar; + QSlider* m_Slider_Size; + QTableWidget *gameList; + std::unique_ptr m_xgrid; + // Actions regarding showing/hiding columns QAction* showIconColAct; QAction* showNameColAct; @@ -134,7 +110,41 @@ private: QAction* showPathColAct; QList columnActs; + + // Actions regarding showing/hiding categories + Tool_Bar_Button m_catActHDD; + Tool_Bar_Button m_catActDisc; + Tool_Bar_Button m_catActHome; + Tool_Bar_Button m_catActGameData; + Tool_Bar_Button m_catActAudioVideo; + Tool_Bar_Button m_catActUnknown; + + QList m_categoryButtons; + + QActionGroup* m_categoryActs; + + // Actions regarding switching list modes + Tool_Bar_Button m_modeActList; + Tool_Bar_Button m_modeActGrid; + + QActionGroup* m_modeActs; + + // TODO: Reorganize this into a sensible order for private variables. std::shared_ptr xgui_settings; + + int m_sortColumn; + Qt::SortOrder m_colSortOrder; + bool m_isListLayout = true; + bool m_showToolBar = true; + std::vector m_game_data; + QSize m_Icon_Size; + QString m_Icon_Size_Str; + qreal m_Margin_Factor; + qreal m_Text_Factor; + QStringList m_categoryFilters; + Render_Creator m_Render_Creator; + + uint m_games_per_row = 0; }; #endif // GAMELISTFRAME_H diff --git a/rpcs3/rpcs3qt/game_list_grid.cpp b/rpcs3/rpcs3qt/game_list_grid.cpp new file mode 100644 index 0000000000..c2d28199fb --- /dev/null +++ b/rpcs3/rpcs3qt/game_list_grid.cpp @@ -0,0 +1,89 @@ +#include "game_list_grid.h" +#include "game_list_grid_delegate.h" + +#include + +game_list_grid::game_list_grid(const QSize& icon_size, const qreal& margin_factor, const qreal& text_factor, const bool& showText) + : QTableWidget(), m_icon_size(icon_size), m_margin_factor(margin_factor), m_text_factor(text_factor), m_text_enabled(showText) +{ + QSize item_size; + if (m_text_enabled) + { + item_size = m_icon_size + QSize(m_icon_size.width() * m_margin_factor * 2, m_icon_size.height() * m_margin_factor * (m_text_factor + 1)); + } + else + { + item_size = m_icon_size + m_icon_size * m_margin_factor * 2; + } + + grid_item_delegate = new game_list_grid_delegate(item_size, m_margin_factor, m_text_factor, this); + setItemDelegate(grid_item_delegate); + setSelectionBehavior(QAbstractItemView::SelectItems); + setSelectionMode(QAbstractItemView::SingleSelection); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + setContextMenuPolicy(Qt::CustomContextMenu); + verticalHeader()->setVisible(false); + horizontalHeader()->setVisible(false); + setShowGrid(false); +} + +game_list_grid::~game_list_grid() +{ +} + +void game_list_grid::enableText(const bool& enabled) +{ + m_text_enabled = enabled; +} + +void game_list_grid::setIconSize(const QSize& size) +{ + if (m_text_enabled) + { + grid_item_delegate->setItemSize(size + QSize(size.width() * m_margin_factor * 2, size.height() * m_margin_factor * (m_text_factor + 1))); + } + else + { + grid_item_delegate->setItemSize(size + size * m_margin_factor * 2); + } +} + +void game_list_grid::addItem(const QPixmap& img, const QString& name, const int& idx, const int& row, const int& col) +{ + // define size of expanded image, which is raw image size + margins + QSize exp_size; + if (m_text_enabled) + { + exp_size = m_icon_size + QSize(m_icon_size.width() * m_margin_factor * 2, m_icon_size.height() * m_margin_factor * (m_text_factor + 1)); + } + else + { + exp_size = m_icon_size + m_icon_size * m_margin_factor * 2; + } + + // define offset for raw image placement + QPoint offset = QPoint(m_icon_size.width() * m_margin_factor, m_icon_size.height() * m_margin_factor); + + // create empty canvas for expanded image + QImage exp_img = QImage(exp_size, QImage::Format_ARGB32); + exp_img.fill(Qt::transparent); + + // place raw image inside expanded image + QPainter painter(&exp_img); + painter.drawPixmap(offset, img); + painter.end(); + + // create item with expanded image, title and position + QTableWidgetItem* item = new QTableWidgetItem(); + item->setData(Qt::ItemDataRole::DecorationRole, QPixmap::fromImage(exp_img)); + item->setData(Qt::ItemDataRole::UserRole, idx); + item->setData(Qt::ItemDataRole::ToolTipRole, name); + if (m_text_enabled) { item->setData(Qt::ItemDataRole::DisplayRole, name); } + setItem(row, col, item); +} + +qreal game_list_grid::getMarginFactor() +{ + return m_margin_factor; +} diff --git a/rpcs3/rpcs3qt/game_list_grid.h b/rpcs3/rpcs3qt/game_list_grid.h new file mode 100644 index 0000000000..2a27cf22cd --- /dev/null +++ b/rpcs3/rpcs3qt/game_list_grid.h @@ -0,0 +1,36 @@ +#ifndef GAME_LIST_GRID_H +#define GAME_LIST_GRID_H + +#include "game_list_grid_delegate.h" + +#include +#include +#include +#include +#include +#include + +class game_list_grid : public QTableWidget +{ + Q_OBJECT + + QSize m_icon_size; + qreal m_margin_factor; + qreal m_text_factor; + bool m_text_enabled = true; + +public: + explicit game_list_grid(const QSize& icon_size, const qreal& margin_factor, const qreal& text_factor, const bool& showText); + ~game_list_grid(); + + void enableText(const bool& enabled); + void setIconSize(const QSize& size); + void addItem(const QPixmap& img, const QString& name, const int& idx, const int& row, const int& col); + + qreal getMarginFactor(); + +private: + game_list_grid_delegate* grid_item_delegate; +}; + +#endif \ No newline at end of file diff --git a/rpcs3/rpcs3qt/game_list_grid_delegate.cpp b/rpcs3/rpcs3qt/game_list_grid_delegate.cpp new file mode 100644 index 0000000000..0547b8bd4b --- /dev/null +++ b/rpcs3/rpcs3qt/game_list_grid_delegate.cpp @@ -0,0 +1,54 @@ +#include "game_list_grid_delegate.h" + +game_list_grid_delegate::game_list_grid_delegate(const QSize& size, const qreal& margin_factor, const qreal& text_factor, QObject *parent) + : QAbstractItemDelegate(parent), m_size(size), m_margin_factor(margin_factor), m_text_factor(text_factor) +{ +} + +game_list_grid_delegate::~game_list_grid_delegate() +{ +} + +void game_list_grid_delegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const +{ + QRect r = option.rect; + + painter->eraseRect(r); + + //Color: #333 + QPen fontPen(QColor::fromRgb(51, 51, 51), 1, Qt::SolidLine); + + //Get title and image + QPixmap image = (qvariant_cast(index.data(Qt::DecorationRole))); + QString title = index.data(Qt::DisplayRole).toString(); + + // image + if (image.isNull() == false) + { + painter->drawPixmap(r, image); + } + + // Add selection overlay + if (option.state & QStyle::State_Selected) + { + QLinearGradient gradientSelected(r.left(), r.top(), r.left(), r.height() + r.top()); + gradientSelected.setColorAt(0.0, QColor::fromRgba(qRgba(119, 213, 247, 128))); + gradientSelected.setColorAt(0.9, QColor::fromRgba(qRgba(27, 134, 183, 128))); + gradientSelected.setColorAt(1.0, QColor::fromRgba(qRgba(0, 120, 174, 128))); + painter->fillRect(r, gradientSelected); + } + + int h = r.height() / (1 + m_margin_factor + m_margin_factor*m_text_factor); + int height = r.height() - h - h * m_margin_factor; + int top = r.bottom() - height; + + // title + painter->setPen(fontPen); + painter->setFont(QFont("Lucida Grande", 8, QFont::DemiBold)); + painter->drawText(QRect(r.left(), top, r.width(), height), Qt::TextWordWrap | Qt::AlignCenter, title); +} + +QSize game_list_grid_delegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const +{ + return m_size; +} diff --git a/rpcs3/rpcs3qt/game_list_grid_delegate.h b/rpcs3/rpcs3qt/game_list_grid_delegate.h new file mode 100644 index 0000000000..16ecb41b37 --- /dev/null +++ b/rpcs3/rpcs3qt/game_list_grid_delegate.h @@ -0,0 +1,22 @@ +#ifndef GAME_LIST_GRID_DELEGATE +#define GAME_LIST_GRID_DELEGATE + +#include +#include + +class game_list_grid_delegate : public QAbstractItemDelegate +{ +public: + game_list_grid_delegate(const QSize& imageSize, const qreal& margin_factor, const qreal& margin_ratio, QObject *parent = 0); + + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const; + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const; + void setItemSize(const QSize& size) { m_size = size; }; + virtual ~game_list_grid_delegate(); +private: + QSize m_size; + qreal m_margin_factor; + qreal m_text_factor; +}; + +#endif diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h index 39ae3c8f6c..68acf31a8c 100644 --- a/rpcs3/rpcs3qt/gui_settings.h +++ b/rpcs3/rpcs3qt/gui_settings.h @@ -28,15 +28,19 @@ typedef struct GUI_SAVE typedef std::map icon_size; typedef QPair q_string_pair; +typedef QPair q_size_pair; typedef QList q_pair_list; +typedef QList q_size_list; namespace GUI { + const QString gl_icon_key_tiny = "tiny"; const QString gl_icon_key_small = "small"; const QString gl_icon_key_medium = "medium"; const QString gl_icon_key_large = "large"; - const icon_size gl_icon_size = { + const q_size_list gl_icon_size = { + { gl_icon_key_tiny, QSize(40, 22) }, { gl_icon_key_small, QSize(80, 44) }, { gl_icon_key_medium, QSize(160, 88) }, { gl_icon_key_large, QSize(320, 176) } @@ -50,9 +54,9 @@ namespace GUI const GUI_SAVE rg_freeze = GUI_SAVE(main_window, "recentGamesFrozen", false); const GUI_SAVE rg_entries = GUI_SAVE(main_window, "recentGamesNames", QVariant::fromValue(q_pair_list())); - const GUI_SAVE ib_pkg_success = GUI_SAVE( main_window, "infoBoxEnabledInstallPKG", true ); - const GUI_SAVE ib_pup_success = GUI_SAVE( main_window, "infoBoxEnabledInstallPUP", true ); - const GUI_SAVE ib_show_welcome = GUI_SAVE(main_window, "infoBoxEnabledWelcome", true); + const GUI_SAVE ib_pkg_success = GUI_SAVE( main_window, "infoBoxEnabledInstallPKG", true ); + const GUI_SAVE ib_pup_success = GUI_SAVE( main_window, "infoBoxEnabledInstallPUP", true ); + const GUI_SAVE ib_show_welcome = GUI_SAVE( main_window, "infoBoxEnabledWelcome", true ); const GUI_SAVE fd_install_pkg = GUI_SAVE( main_window, "lastExplorePathPKG", "" ); const GUI_SAVE fd_install_pup = GUI_SAVE( main_window, "lastExplorePathPUP", "" ); @@ -75,10 +79,14 @@ namespace GUI const GUI_SAVE cat_unknown = GUI_SAVE( game_list, "categoryVisibleUnknown", true ); const GUI_SAVE cat_other = GUI_SAVE( game_list, "categoryVisibleOther", true ); - const GUI_SAVE gl_sortAsc = GUI_SAVE( game_list, "sortAsc", true ); - const GUI_SAVE gl_sortCol = GUI_SAVE( game_list, "sortCol", 1 ); - const GUI_SAVE gl_state = GUI_SAVE( game_list, "state", QByteArray() ); - const GUI_SAVE gl_iconSize = GUI_SAVE( game_list, "iconSize", gl_icon_key_small); + const GUI_SAVE gl_sortAsc = GUI_SAVE( game_list, "sortAsc", true ); + const GUI_SAVE gl_sortCol = GUI_SAVE( game_list, "sortCol", 1 ); + const GUI_SAVE gl_state = GUI_SAVE( game_list, "state", QByteArray() ); + const GUI_SAVE gl_iconSize = GUI_SAVE( game_list, "iconSize", gl_icon_key_small ); + const GUI_SAVE gl_listMode = GUI_SAVE( game_list, "listMode", true ); + const GUI_SAVE gl_textFactor = GUI_SAVE( game_list, "textFactor", (qreal) 2.0 ); + const GUI_SAVE gl_marginFactor = GUI_SAVE( game_list, "marginFactor", (qreal) 0.09 ); + const GUI_SAVE gl_toolBarVisible = GUI_SAVE( game_list, "toolBarVisible", true ); const GUI_SAVE l_tty = GUI_SAVE( logger, "TTY", true ); const GUI_SAVE l_level = GUI_SAVE( logger, "level", (uint)(logs::level::success) ); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 1d7bc7bf93..7089c6521f 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -351,7 +351,7 @@ void main_window::InstallPkg() QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) { fs::remove_all(local_path); - gameListFrame->Refresh(); + gameListFrame->Refresh(true); LOG_SUCCESS(LOADER, "PKG: removed incomplete installation in %s", local_path); return; } @@ -377,7 +377,7 @@ void main_window::InstallPkg() if (progress >= 1.) { - gameListFrame->Refresh(); + gameListFrame->Refresh(true); LOG_SUCCESS(GENERAL, "Successfully installed %s.", fileName); guiSettings->ShowInfoBox(GUI::ib_pkg_success, tr("Success!"), tr("Successfully installed software from package!"), this); @@ -1027,6 +1027,9 @@ void main_window::CreateActions() showControlsAct = new QAction(tr("Show Controls"), this); showControlsAct->setCheckable(true); + showGameListToolBarAct = new QAction(tr("Show Tool Bar"), this); + showGameListToolBarAct->setCheckable(true); + refreshGameListAct = new QAction(tr("&Refresh Game List"), this); showCatHDDGameAct = new QAction(category::hdd_Game, this); @@ -1047,6 +1050,18 @@ void main_window::CreateActions() showCatUnknownAct = new QAction(category::unknown, this); showCatUnknownAct->setCheckable(true); + categoryVisibleActGroup = new QActionGroup(this); + categoryVisibleActGroup->addAction(showCatHDDGameAct); + categoryVisibleActGroup->addAction(showCatDiscGameAct); + categoryVisibleActGroup->addAction(showCatHomeAct); + categoryVisibleActGroup->addAction(showCatAudioVideoAct); + categoryVisibleActGroup->addAction(showCatGameDataAct); + categoryVisibleActGroup->addAction(showCatUnknownAct); + categoryVisibleActGroup->setExclusive(false); + + setIconSizeTinyAct = new QAction(tr("Tiny"), this); + setIconSizeTinyAct->setCheckable(true); + setIconSizeSmallAct = new QAction(tr("Small"), this); setIconSizeSmallAct->setCheckable(true); @@ -1057,11 +1072,23 @@ void main_window::CreateActions() setIconSizeLargeAct->setCheckable(true); iconSizeActGroup = new QActionGroup(this); + iconSizeActGroup->addAction(setIconSizeTinyAct); iconSizeActGroup->addAction(setIconSizeSmallAct); iconSizeActGroup->addAction(setIconSizeMediumAct); iconSizeActGroup->addAction(setIconSizeLargeAct); setIconSizeSmallAct->setChecked(true); + setlistModeListAct = new QAction(tr("List"), this); + setlistModeListAct->setCheckable(true); + + setlistModeGridAct = new QAction(tr("Grid"), this); + setlistModeGridAct->setCheckable(true); + + listModeActGroup = new QActionGroup(this); + listModeActGroup->addAction(setlistModeListAct); + listModeActGroup->addAction(setlistModeGridAct); + setlistModeListAct->setChecked(true); + aboutAct = new QAction(tr("&About"), this); aboutAct->setStatusTip(tr("Show the application's About box")); @@ -1166,32 +1193,27 @@ void main_window::CreateConnects() checked ? controls->show() : controls->hide(); guiSettings->SetValue(GUI::mw_controls, checked); }); + connect(showGameListToolBarAct, &QAction::triggered, this, [=](bool checked){ + gameListFrame->SetToolBarVisible(checked); + }); connect(refreshGameListAct, &QAction::triggered, [=](){ - gameListFrame->Refresh(); + gameListFrame->Refresh(true); }); - connect(showCatHDDGameAct, &QAction::triggered, [=](bool checked){ - gameListFrame->ToggleCategoryFilter(category::hdd_Game, checked); - guiSettings->SetCategoryVisibility(category::hdd_Game, checked); - }); - connect(showCatDiscGameAct, &QAction::triggered, [=](bool checked){ - gameListFrame->ToggleCategoryFilter(category::disc_Game, checked); - guiSettings->SetCategoryVisibility(category::disc_Game, checked); - }); - connect(showCatHomeAct, &QAction::triggered, [=](bool checked){ - gameListFrame->ToggleCategoryFilter(category::home, checked); - guiSettings->SetCategoryVisibility(category::home, checked); - }); - connect(showCatAudioVideoAct, &QAction::triggered, [=](bool checked){ - gameListFrame->ToggleCategoryFilter(category::audio_Video, checked); - guiSettings->SetCategoryVisibility(category::audio_Video, checked); - }); - connect(showCatGameDataAct, &QAction::triggered, [=](bool checked){ - gameListFrame->ToggleCategoryFilter(category::game_Data, checked); - guiSettings->SetCategoryVisibility(category::game_Data, checked); - }); - connect(showCatUnknownAct, &QAction::triggered, [=](bool checked) { - gameListFrame->ToggleCategoryFilter(category::unknown, checked); - guiSettings->SetCategoryVisibility(category::unknown, checked); + connect(categoryVisibleActGroup, &QActionGroup::triggered, [=](QAction* act) + { + QString cat; + const bool& checked = act->isChecked(); + + if (act == showCatHDDGameAct) cat = category::hdd_Game; + else if (act == showCatDiscGameAct) cat = category::disc_Game; + else if (act == showCatHomeAct) cat = category::home; + else if (act == showCatAudioVideoAct) cat = category::audio_Video; + else if (act == showCatGameDataAct) cat = category::game_Data; + else if (act == showCatUnknownAct) cat = category::unknown; + + gameListFrame->SetCategoryActIcon(categoryVisibleActGroup->actions().indexOf(act), checked); + gameListFrame->ToggleCategoryFilter(cat, checked); + guiSettings->SetCategoryVisibility(cat, checked); }); connect(aboutAct, &QAction::triggered, this, &main_window::About); connect(aboutQtAct, &QAction::triggered, qApp, &QApplication::aboutQt); @@ -1207,10 +1229,37 @@ void main_window::CreateConnects() if (act == setIconSizeLargeAct) key = GUI::gl_icon_key_large; else if (act == setIconSizeMediumAct) key = GUI::gl_icon_key_medium; - else key = GUI::gl_icon_key_small; + else if (act == setIconSizeSmallAct) key = GUI::gl_icon_key_small; + else key = GUI::gl_icon_key_tiny; guiSettings->SetValue(GUI::gl_iconSize, key); - gameListFrame->ResizeIcons(GUI::gl_icon_size.at(key)); + + for (int i = 0; i < GUI::gl_icon_size.count(); i++) + { + if (GUI::gl_icon_size.at(i).first == key) + { + gameListFrame->ResizeIcons(GUI::gl_icon_size.at(i).second, i); + break; + } + } + }); + connect (gameListFrame, &game_list_frame::RequestIconSizeActSet, [=](const int& idx) + { + iconSizeActGroup->actions().at(idx)->trigger(); + }); + connect(gameListFrame, &game_list_frame::RequestListModeActSet, [=](const bool& isList) + { + isList ? setlistModeListAct->trigger() : setlistModeGridAct->trigger(); + }); + connect(gameListFrame, &game_list_frame::RequestCategoryActSet, [=](const int& id) + { + categoryVisibleActGroup->actions().at(id)->trigger(); + }); + connect(listModeActGroup, &QActionGroup::triggered, [=](QAction* act) + { + bool isList = act == setlistModeListAct; + gameListFrame->SetListMode(isList); + categoryVisibleActGroup->setEnabled(isList); }); } @@ -1261,20 +1310,17 @@ void main_window::CreateMenus() viewMenu->addAction(showControlsAct); viewMenu->addSeparator(); viewMenu->addAction(showGameListAct); + viewMenu->addAction(showGameListToolBarAct); viewMenu->addAction(refreshGameListAct); QMenu *categoryMenu = viewMenu->addMenu(tr("Show Categories")); - categoryMenu->addAction(showCatHDDGameAct); - categoryMenu->addAction(showCatDiscGameAct); - categoryMenu->addAction(showCatHomeAct); - categoryMenu->addAction(showCatAudioVideoAct); - categoryMenu->addAction(showCatGameDataAct); - categoryMenu->addAction(showCatUnknownAct); + categoryMenu->addActions(categoryVisibleActGroup->actions()); QMenu *iconSizeMenu = viewMenu->addMenu(tr("Icon Size")); - iconSizeMenu->addAction(setIconSizeSmallAct); - iconSizeMenu->addAction(setIconSizeMediumAct); - iconSizeMenu->addAction(setIconSizeLargeAct); + iconSizeMenu->addActions(iconSizeActGroup->actions()); + + QMenu *listModeMenu = viewMenu->addMenu(tr("Game List Mode")); + listModeMenu->addActions(listModeActGroup->actions()); QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(aboutAct); @@ -1385,6 +1431,7 @@ void main_window::ConfigureGuiFromSettings(bool configureAll) showGameListAct->setChecked(guiSettings->GetValue(GUI::mw_gamelist).toBool()); showDebuggerAct->setChecked(guiSettings->GetValue(GUI::mw_debugger).toBool()); showControlsAct->setChecked(guiSettings->GetValue(GUI::mw_controls).toBool()); + showGameListToolBarAct->setChecked(guiSettings->GetValue(GUI::gl_toolBarVisible).toBool()); guiSettings->GetValue(GUI::mw_controls).toBool() ? controls->show() : controls->hide(); showCatHDDGameAct->setChecked(guiSettings->GetCategoryVisibility(category::hdd_Game)); @@ -1397,7 +1444,15 @@ void main_window::ConfigureGuiFromSettings(bool configureAll) QString key = guiSettings->GetValue(GUI::gl_iconSize).toString(); if (key == GUI::gl_icon_key_large) setIconSizeLargeAct->setChecked(true); else if (key == GUI::gl_icon_key_medium) setIconSizeMediumAct->setChecked(true); - else setIconSizeSmallAct->setChecked(true); + else if (key == GUI::gl_icon_key_small) setIconSizeSmallAct->setChecked(true); + else setIconSizeTinyAct->setChecked(true); + + + bool isListMode = guiSettings->GetValue(GUI::gl_listMode).toBool(); + if (isListMode) setlistModeListAct->setChecked(true); + else setlistModeGridAct->setChecked(true); + categoryVisibleActGroup->setEnabled(isListMode); + if (configureAll) { diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index 0ae1989fab..ab9c7a5aa5 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -98,6 +98,8 @@ private: QList m_recentGameActs; QActionGroup* iconSizeActGroup; + QActionGroup* listModeActGroup; + QActionGroup* categoryVisibleActGroup; QAction *bootElfAct; QAction *bootGameAct; @@ -125,15 +127,19 @@ private: QAction *showGameListAct; QAction *showControlsAct; QAction *refreshGameListAct; + QAction *showGameListToolBarAct; QAction* showCatHDDGameAct; QAction* showCatDiscGameAct; QAction* showCatHomeAct; QAction* showCatAudioVideoAct; QAction* showCatGameDataAct; QAction* showCatUnknownAct; + QAction* setIconSizeTinyAct; QAction* setIconSizeSmallAct; QAction* setIconSizeMediumAct; QAction* setIconSizeLargeAct; + QAction* setlistModeListAct; + QAction* setlistModeGridAct; QAction *aboutAct; QAction *aboutQtAct;