diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 9309e0f66a..4be148aeab 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -411,6 +411,11 @@ true true + + true + true + true + true true @@ -576,6 +581,11 @@ true true + + true + true + true + true true @@ -761,6 +771,11 @@ true true + + true + true + true + true true @@ -926,6 +941,11 @@ true true + + true + true + true + true true @@ -1025,6 +1045,7 @@ + @@ -1466,6 +1487,24 @@ .\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_SDK)\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" + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DBRANCH= -DLLVM_AVAILABLE -DPUGIXML_HEADER_ONLY -D_ENABLE_EXTENDED_ALIGNED_STORAGE -D_UNICODE "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-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" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\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_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -D_SCL_SECURE_NO_WARNINGS -DPUGIXML_HEADER_ONLY -D_ENABLE_EXTENDED_ALIGNED_STORAGE -D_UNICODE "-I$(VULKAN_SDK)\Include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-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" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DPUGIXML_HEADER_ONLY -D_ENABLE_EXTENDED_ALIGNED_STORAGE -D_UNICODE "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-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" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\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_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -DPUGIXML_HEADER_ONLY -D_ENABLE_EXTENDED_ALIGNED_STORAGE -D_UNICODE "-I$(VULKAN_SDK)\Include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-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" + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 93014d3547..5d45bf67f2 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -674,6 +674,21 @@ Generated Files\Debug - LLVM + + Gui\misc dialogs + + + Generated Files\Release - LLVM + + + Generated Files\Debug + + + Generated Files\Release + + + Generated Files\Debug - LLVM + @@ -894,6 +909,9 @@ Gui\trophy + + Gui\misc dialogs + diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 4f372f147c..0282109f86 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -3,6 +3,7 @@ #include "settings_dialog.h" #include "table_item_delegate.h" #include "custom_table_widget_item.h" +#include "input_dialog.h" #include "Emu/Memory/vm.h" #include "Emu/System.h" @@ -423,6 +424,7 @@ void game_list_frame::Refresh(const bool fromDrive, const bool scrollAfter) QString serial = qstr(game.serial); m_notes[serial] = m_gui_settings->GetValue(gui::notes, serial, "").toString(); + m_titles[serial] = m_gui_settings->GetValue(gui::titles, serial, "").toString().simplified(); serials.insert(serial); auto cat = category::cat_boot.find(game.category); @@ -469,13 +471,15 @@ void game_list_frame::Refresh(const bool fromDrive, const bool scrollAfter) // Blame MSVC for double }} }} - auto op = [](const game_info& game1, const game_info& game2) - { - return qstr(game1->info.name).toLower() < qstr(game2->info.name).toLower(); - }; - // Sort by name at the very least. - std::sort(m_game_data.begin(), m_game_data.end(), op); + std::sort(m_game_data.begin(), m_game_data.end(), [&](const game_info& game1, const game_info& game2) + { + const QString custom_title1 = m_titles[qstr(game1->info.serial)]; + const QString custom_title2 = m_titles[qstr(game2->info.serial)]; + const QString title1 = custom_title1.isEmpty() ? qstr(game1->info.name) : custom_title1; + const QString title2 = custom_title2.isEmpty() ? qstr(game2->info.name) : custom_title2; + return title1.toLower() < title2.toLower(); + }); // clean up hidden games list m_hidden_list.intersect(serials); @@ -618,6 +622,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) QAction* configure = myMenu.addAction(tr("&Configure")); QAction* createPPUCache = myMenu.addAction(tr("&Create PPU Cache")); myMenu.addSeparator(); + QAction* renameTitle = myMenu.addAction(tr("&Rename In Game List")); QAction* hide_serial = myMenu.addAction(tr("&Hide From Game List")); hide_serial->setCheckable(true); hide_serial->setChecked(m_hidden_list.contains(serial)); @@ -742,16 +747,52 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) { m_game_compat->RequestCompatibility(true); }); + connect(renameTitle, &QAction::triggered, [=] + { + const QString custom_title = m_gui_settings->GetValue(gui::titles, serial, "").toString(); + const QString old_title = custom_title.isEmpty() ? name : custom_title; + QString new_title; + + input_dialog dlg(128, old_title, tr("Rename Title"), tr("%0\n%1\n\nYou can clear the line in order to use the original title.").arg(name).arg(serial), name, this); + dlg.move(globalPos); + connect(&dlg, &input_dialog::text_changed, this, [&new_title](const QString& text) + { + new_title = text.simplified(); + }); + + if (dlg.exec() == QDialog::Accepted) + { + if (new_title.isEmpty() || new_title == name) + { + m_titles.remove(serial); + m_gui_settings->RemoveValue(gui::titles, serial); + } + else + { + m_titles[serial] = new_title; + m_gui_settings->SetValue(gui::titles, serial, new_title); + } + Refresh(true); // full refresh in order to reliably sort the list + } + }); connect(editNotes, &QAction::triggered, [=] { bool accepted; const QString old_notes = m_gui_settings->GetValue(gui::notes, serial, "").toString(); - const QString new_notes = QInputDialog::getMultiLineText(this, tr("Edit Tooltip Notes"), QString("%0\n%1").arg(name).arg(serial), old_notes, &accepted); + const QString new_notes = QInputDialog::getMultiLineText(this, tr("Edit Tooltip Notes"), tr("%0\n%1").arg(name).arg(serial), old_notes, &accepted); if (accepted) { - m_notes[serial] = new_notes; - m_gui_settings->SetValue(gui::notes, serial, new_notes); + if (new_notes.simplified().isEmpty()) + { + m_notes.remove(serial); + m_gui_settings->RemoveValue(gui::notes, serial); + } + else + { + m_notes[serial] = new_notes; + m_gui_settings->SetValue(gui::notes, serial, new_notes); + } Refresh(); } }); @@ -1144,8 +1185,9 @@ int game_list_frame::PopulateGameList() if (!IsEntryVisible(game)) continue; - const QString name = qstr(game->info.name).simplified(); const QString serial = qstr(game->info.serial); + const QString custom_title = m_titles[serial]; + const QString title = custom_title.isEmpty() ? qstr(game->info.name) : custom_title; const QString notes = m_notes[serial]; // Icon @@ -1155,7 +1197,7 @@ int game_list_frame::PopulateGameList() icon_item->setData(gui::game_role, QVariant::fromValue(game)); // Title - custom_table_widget_item* title_item = new custom_table_widget_item(game->info.name); + custom_table_widget_item* title_item = new custom_table_widget_item(title); if (game->hasCustomConfig) { title_item->setIcon(QIcon(":/Icons/custom_config.png")); @@ -1166,7 +1208,7 @@ int game_list_frame::PopulateGameList() if (!notes.isEmpty()) { - const QString tool_tip = tr("%0 [%1]\n\nNotes:\n%2").arg(name).arg(serial).arg(notes); + const QString tool_tip = tr("%0 [%1]\n\nNotes:\n%2").arg(title).arg(serial).arg(notes); title_item->setToolTip(tool_tip); serial_item->setToolTip(tool_tip); } @@ -1245,7 +1287,7 @@ void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, con // Edge cases! if (entries == 0) - { // For whatever reason, 0%x is division by zero. Absolute nonsense by definition of modulus. But, I'll acquiesce. + { // For whatever reason, 0%x is division by zero. Absolute nonsense by definition of modulus. But, I'll acquiesce. return; } @@ -1256,15 +1298,14 @@ void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, con m_xgrid->setRowCount(maxRows); m_xgrid->setColumnCount(maxCols); - QString title, serial, notes; - for (const auto& app : matching_apps) { - title = qstr(app->info.name).simplified(); // simplified() forces single line text - serial = qstr(app->info.serial); - notes = m_notes[serial]; + const QString serial = qstr(app->info.serial); + const QString custom_title = m_titles[serial]; + const QString title = custom_title.isEmpty() ? qstr(app->info.name) : custom_title; + const QString notes = m_notes[serial]; - m_xgrid->addItem(app->pxmap, title, r, c); + m_xgrid->addItem(app->pxmap, title, r, c); m_xgrid->item(r, c)->setData(gui::game_role, QVariant::fromValue(app)); if (!notes.isEmpty()) diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index c355a40582..7377a99ddd 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -262,6 +262,7 @@ private: Qt::SortOrder m_colSortOrder; int m_sortColumn; QMap m_notes; + QMap m_titles; // Categories QStringList m_categoryFilters; diff --git a/rpcs3/rpcs3qt/gui_settings.cpp b/rpcs3/rpcs3qt/gui_settings.cpp index f7f7c9577c..b46bc99000 100644 --- a/rpcs3/rpcs3qt/gui_settings.cpp +++ b/rpcs3/rpcs3qt/gui_settings.cpp @@ -109,6 +109,13 @@ void gui_settings::Reset(bool removeMeta) } } +void gui_settings::RemoveValue(const QString& key, const QString& name) +{ + m_settings.beginGroup(key); + m_settings.remove(name); + m_settings.endGroup(); +} + QVariant gui_settings::GetValue(const gui_save& entry) { return m_settings.value(entry.key + "/" + entry.name, entry.def); diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h index 579815b315..50f20efe50 100644 --- a/rpcs3/rpcs3qt/gui_settings.h +++ b/rpcs3/rpcs3qt/gui_settings.h @@ -129,6 +129,7 @@ namespace gui const QString savedata = "SaveData"; const QString users = "Users"; const QString notes = "Notes"; + const QString titles = "Titles"; const QColor gl_icon_color = QColor(36, 36, 36, 255); @@ -262,6 +263,9 @@ public: public Q_SLOTS: void Reset(bool removeMeta = false); + /** Remove entry */ + void RemoveValue(const QString& key, const QString& name); + /** Write value to entry */ void SetValue(const gui_save& entry, const QVariant& value); void SetValue(const QString& key, const QString& name, const QVariant& value); diff --git a/rpcs3/rpcs3qt/input_dialog.cpp b/rpcs3/rpcs3qt/input_dialog.cpp new file mode 100644 index 0000000000..edd590d5cd --- /dev/null +++ b/rpcs3/rpcs3qt/input_dialog.cpp @@ -0,0 +1,33 @@ +#include "input_dialog.h" + +#include +#include + +input_dialog::input_dialog(int max_length, const QString& text, const QString& title, const QString& label, const QString& placeholder, QWidget *parent, Qt::WindowFlags f) + : QDialog(parent, f) +{ + setWindowTitle(title); + + QLabel* m_label = new QLabel(label); + + QLineEdit* m_input = new QLineEdit(); + m_input->setPlaceholderText(placeholder); + m_input->setText(text); + m_input->setMaxLength(max_length); + m_input->setClearButtonEnabled(true); + connect(m_input, &QLineEdit::textChanged, this, &input_dialog::text_changed); + + QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->addWidget(m_label); + layout->addWidget(m_input); + layout->addWidget(button_box); + setLayout(layout); + + setFixedHeight(sizeHint().height()); +} + +input_dialog::~input_dialog(){} diff --git a/rpcs3/rpcs3qt/input_dialog.h b/rpcs3/rpcs3qt/input_dialog.h new file mode 100644 index 0000000000..922d1e9def --- /dev/null +++ b/rpcs3/rpcs3qt/input_dialog.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include + +class input_dialog : public QDialog +{ + Q_OBJECT + +public: + input_dialog(int max_length, const QString& text, const QString& title, const QString& label, const QString& placeholder, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + ~input_dialog(); + +private: + QString m_text; + +Q_SIGNALS: + void text_changed(const QString& text); +};