diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index c0fe01d094..c48c509852 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include inline std::string sstr(const QString& _in) { return _in.toStdString(); } @@ -355,6 +357,7 @@ void game_list_frame::Refresh(const bool fromDrive, const bool scrollAfter) // Load PSF m_game_data.clear(); + m_notes.clear(); const std::string _hdd = Emu.GetHddDir(); @@ -417,7 +420,9 @@ void game_list_frame::Refresh(const bool fromDrive, const bool scrollAfter) continue; } - serials.insert(qstr(game.serial)); + QString serial = qstr(game.serial); + m_notes[serial] = m_gui_settings->GetValue(gui::notes, serial, "").toString(); + serials.insert(serial); auto cat = category::cat_boot.find(game.category); if (cat != category::cat_boot.end()) @@ -607,6 +612,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) GameInfo currGame = gameinfo->info; const QString serial = qstr(currGame.serial); + const QString name = qstr(currGame.name).simplified(); // Make Actions QMenu myMenu; @@ -631,6 +637,8 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) myMenu.addSeparator(); QAction* checkCompat = myMenu.addAction(tr("&Check Game Compatibility")); QAction* downloadCompat = myMenu.addAction(tr("&Download Compatibility Database")); + myMenu.addSeparator(); + QAction* editNotes = myMenu.addAction(tr("&Edit Tooltip Notes")); const std::string config_base_dir = fs::get_config_dir() + "data/" + currGame.serial; @@ -672,7 +680,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) return; } - QMessageBox* mb = new QMessageBox(QMessageBox::Question, tr("Confirm %1 Removal").arg(qstr(currGame.category)), tr("Permanently remove %0 from drive?\nPath: %1").arg(qstr(currGame.name)).arg(qstr(currGame.path)), QMessageBox::Yes | QMessageBox::No, this); + QMessageBox* mb = new QMessageBox(QMessageBox::Question, tr("Confirm %1 Removal").arg(qstr(currGame.category)), tr("Permanently remove %0 from drive?\nPath: %1").arg(name).arg(qstr(currGame.path)), QMessageBox::Yes | QMessageBox::No, this); mb->setCheckBox(new QCheckBox(tr("Remove caches and custom config"))); mb->deleteLater(); if (mb->exec() == QMessageBox::Yes) @@ -721,13 +729,26 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) }); connect(checkCompat, &QAction::triggered, [=] { - QString link = "https://rpcs3.net/compatibility?g=" + qstr(currGame.serial); + QString link = "https://rpcs3.net/compatibility?g=" + serial; QDesktopServices::openUrl(QUrl(link)); }); connect(downloadCompat, &QAction::triggered, [=] { m_game_compat->RequestCompatibility(true); }); + 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); + + if (accepted) + { + m_notes[serial] = new_notes; + m_gui_settings->SetValue(gui::notes, serial, new_notes); + Refresh(); + } + }); //Disable options depending on software category QString category = qstr(currGame.category); @@ -988,6 +1009,33 @@ bool game_list_frame::eventFilter(QObject *object, QEvent *event) } } } + else if (event->type() == QEvent::ToolTip) + { + QHelpEvent *helpEvent = static_cast(event); + QTableWidgetItem* item; + + if (m_isListLayout) + { + item = m_gameList->itemAt(helpEvent->globalPos()); + } + else + { + item = m_xgrid->itemAt(helpEvent->globalPos()); + } + + if (item && !item->toolTip().isEmpty() && (!m_isListLayout || item->column() == gui::column_name || item->column() == gui::column_serial)) + { + QToolTip::showText(helpEvent->globalPos(), item->toolTip()); + } + else + { + QToolTip::hideText(); + event->ignore(); + } + + return true; + } + return QDockWidget::eventFilter(object, event); } @@ -1011,6 +1059,10 @@ 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 notes = m_notes[serial]; + // Icon custom_table_widget_item* icon_item = new custom_table_widget_item; icon_item->setData(Qt::DecorationRole, game->pxmap); @@ -1024,6 +1076,16 @@ int game_list_frame::PopulateGameList() title_item->setIcon(QIcon(":/Icons/cog_black.png")); } + // Serial + custom_table_widget_item* serial_item = new custom_table_widget_item(game->info.serial); + + if (!notes.isEmpty()) + { + const QString tool_tip = tr("%0 [%1]\n\nNotes:\n%2").arg(name).arg(serial).arg(notes); + title_item->setToolTip(tool_tip); + serial_item->setToolTip(tool_tip); + } + // Move Support (http://www.psdevwiki.com/ps3/PARAM.SFO#ATTRIBUTE) bool supports_move = game->info.attr & 0x800000; @@ -1039,7 +1101,7 @@ int game_list_frame::PopulateGameList() m_gameList->setItem(row, gui::column_icon, icon_item); m_gameList->setItem(row, gui::column_name, title_item); - m_gameList->setItem(row, gui::column_serial, new custom_table_widget_item(game->info.serial)); + m_gameList->setItem(row, gui::column_serial, serial_item); m_gameList->setItem(row, gui::column_firmware, new custom_table_widget_item(game->info.fw)); m_gameList->setItem(row, gui::column_version, new custom_table_widget_item(game->info.app_ver)); m_gameList->setItem(row, gui::column_category, new custom_table_widget_item(game->info.category)); @@ -1109,13 +1171,26 @@ 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) { - const QString title = qstr(app->info.name).simplified(); // simplified() forces single line text + title = qstr(app->info.name).simplified(); // simplified() forces single line text + serial = qstr(app->info.serial); + 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()) + { + m_xgrid->item(r, c)->setToolTip(tr("%0 [%1]\n\nNotes:\n%2").arg(title).arg(serial).arg(notes)); + } + else + { + m_xgrid->item(r, c)->setToolTip(tr("%0 [%1]").arg(title).arg(serial)); + } + if (selected_item == app->info.icon_path) { m_xgrid->setCurrentItem(m_xgrid->item(r, c)); diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index 581ca8e656..71b71936f7 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -254,6 +254,7 @@ private: QList m_columnActs; Qt::SortOrder m_colSortOrder; int m_sortColumn; + QMap m_notes; // Categories QStringList m_categoryFilters; diff --git a/rpcs3/rpcs3qt/game_list_grid.cpp b/rpcs3/rpcs3qt/game_list_grid.cpp index 6ba48d2c3f..3ce1535292 100644 --- a/rpcs3/rpcs3qt/game_list_grid.cpp +++ b/rpcs3/rpcs3qt/game_list_grid.cpp @@ -91,7 +91,6 @@ void game_list_grid::addItem(const QPixmap& img, const QString& name, const int& // 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::ToolTipRole, name); if (m_text_enabled) { diff --git a/rpcs3/rpcs3qt/gui_settings.cpp b/rpcs3/rpcs3qt/gui_settings.cpp index c1d30ac063..83b22d44fe 100644 --- a/rpcs3/rpcs3qt/gui_settings.cpp +++ b/rpcs3/rpcs3qt/gui_settings.cpp @@ -62,6 +62,11 @@ QVariant gui_settings::GetValue(const gui_save& entry) return m_settings.value(entry.key + "/" + entry.name, entry.def); } +QVariant gui_settings::GetValue(const QString& key, const QString& name, const QString& def) +{ + return m_settings.value(key + "/" + name, def); +} + QVariant gui_settings::List2Var(const q_pair_list& list) { QByteArray ba; @@ -86,6 +91,13 @@ void gui_settings::SetValue(const gui_save& entry, const QVariant& value) m_settings.endGroup(); } +void gui_settings::SetValue(const QString& key, const QString& name, const QVariant& value) +{ + m_settings.beginGroup(key); + m_settings.setValue(name, value); + m_settings.endGroup(); +} + QStringList gui_settings::GetGameListCategoryFilters() { QStringList filterList; diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h index 26d91b6bbe..231dfe3de6 100644 --- a/rpcs3/rpcs3qt/gui_settings.h +++ b/rpcs3/rpcs3qt/gui_settings.h @@ -126,6 +126,7 @@ namespace gui const QString gs_frame = "GSFrame"; const QString trophy = "Trophy"; const QString savedata = "SaveData"; + const QString notes = "Notes"; const QColor gl_icon_color = QColor(209, 209, 209, 255); const QColor mw_tool_icon_color = QColor( 64, 64, 64, 255); @@ -239,6 +240,7 @@ public: bool GetCategoryVisibility(int cat); QVariant GetValue(const gui_save& entry); + QVariant GetValue(const QString& key, const QString& name, const QString& def); QVariant List2Var(const q_pair_list& list); q_pair_list Var2List(const QVariant &var); @@ -257,6 +259,7 @@ public Q_SLOTS: /** Write value to entry */ void SetValue(const gui_save& entry, const QVariant& value); + void SetValue(const QString& key, const QString& name, const QVariant& value); /** Sets the visibility of the chosen category. */ void SetCategoryVisibility(int cat, const bool& val); @@ -277,5 +280,3 @@ private: QSettings m_settings; QDir m_settingsDir; }; - - static gui_save GetGuiSaveForColumn(int col);