diff --git a/rpcs3/rpcs3qt/update_manager.cpp b/rpcs3/rpcs3qt/update_manager.cpp index a34292ce0e..8f083b6c18 100644 --- a/rpcs3/rpcs3qt/update_manager.cpp +++ b/rpcs3/rpcs3qt/update_manager.cpp @@ -14,11 +14,15 @@ #include #include #include +#include +#include #include +#include #include #include #include #include +#include #if defined(_WIN32) || defined(__APPLE__) #include <7z.h> @@ -277,6 +281,8 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce update_log.notice("JSON changelog entry does not contain a title string."); } + entry.pr = changelog_entry["pr"].toInt(); + m_update_info.changelog.push_back(std::move(entry)); } else @@ -332,7 +338,7 @@ void update_manager::update(bool auto_accept) if (m_update_info.diff_msec < 0) { // This usually means that the current version was marked as broken and won't be shipped anymore, so we need to downgrade to avoid certain bugs. - update_message = tr("A better version of RPCS3 is available!

Current version: %0 (%1)
Better version: %2 (%3)
%4
Do you want to update?") + update_message = tr("A better version of RPCS3 is available!

Current version: %0 (%1)
Better version: %2 (%3)
%4") .arg(m_update_info.old_version) .arg(m_update_info.cur_date) .arg(m_update_info.new_version) @@ -341,7 +347,7 @@ void update_manager::update(bool auto_accept) } else { - update_message = tr("A new version of RPCS3 is available!

Current version: %0 (%1)
Latest version: %2 (%3)
Your version is %4 behind.
%5
Do you want to update?") + update_message = tr("A new version of RPCS3 is available!

Current version: %0 (%1)
Latest version: %2 (%3)
Your version is %4 behind.
%5") .arg(m_update_info.old_version) .arg(m_update_info.cur_date) .arg(m_update_info.new_version) @@ -352,42 +358,133 @@ void update_manager::update(bool auto_accept) } else { - update_message = tr("You're currently using a custom or PR build.

Latest version: %0 (%1)
The latest version is %2 old.
%3
Do you want to update to the latest official RPCS3 version?") + update_message = tr("You're currently using a custom or PR build.

Latest version: %0 (%1)
The latest version is %2 old.
%3") .arg(m_update_info.new_version) .arg(m_update_info.lts_date) .arg(localized.GetVerboseTimeByMs(std::abs(m_update_info.diff_msec), true)) .arg(support_message); } - QString changelog_content; + // Build HTML changelog with clickable PR links when available + QString changelog_html; + QString changelog_html_6; // First 6 entries for height cap + int changelog_count = 0; for (const changelog_data& entry : m_update_info.changelog) { - if (!changelog_content.isEmpty()) - changelog_content.append('\n'); - changelog_content.append(tr("• %0: %1").arg(entry.version.isEmpty() ? tr("N/A") : entry.version, entry.title.isEmpty() ? tr("N/A") : entry.title)); + const QString version_str = entry.version.isEmpty() ? tr("N/A") : entry.version; + const QString title_str = entry.title.isEmpty() ? tr("N/A") : entry.title; + + QString entry_html; + + if (entry.pr > 0) + { + entry_html = tr("  • %0: %1 (#%2)").arg(version_str, title_str, QString::number(entry.pr)); + } + else + { + entry_html = tr("  • %0: %1").arg(version_str, title_str); + } + + if (!changelog_html.isEmpty()) + changelog_html += QStringLiteral("
"); + changelog_html += entry_html; + + if (changelog_count < 6) + { + if (!changelog_html_6.isEmpty()) + changelog_html_6 += QStringLiteral("
"); + changelog_html_6 += entry_html; + } + + changelog_count++; } QMessageBox mb(QMessageBox::Icon::Question, tr("Update Available"), update_message, QMessageBox::Yes | QMessageBox::No, m_downloader->get_progress_dialog() ? m_downloader->get_progress_dialog() : m_parent); mb.setTextFormat(Qt::RichText); mb.setCheckBox(new QCheckBox(tr("Don't show again for this version"))); - if (!changelog_content.isEmpty()) + // Rearrange the layout: checkbox, then changelog, then prompt, then buttons + if (QGridLayout* grid = qobject_cast(mb.layout())) { - mb.setInformativeText(tr("To see the changelog, please click \"Show Details\".")); - mb.setDetailedText(tr("Changelog:\n\n%0").arg(changelog_content)); + const int cols = grid->columnCount(); - // Smartass hack to make the unresizeable message box wide enough for the changelog - const int changelog_width = QLabel(changelog_content).sizeHint().width(); - if (QLabel(update_message).sizeHint().width() < changelog_width) + QDialogButtonBox* button_box = mb.findChild(); + + if (button_box) + grid->removeWidget(button_box); + + int row = grid->rowCount(); + + if (!changelog_html.isEmpty()) { - update_message += "  "; - while (QLabel(update_message).sizeHint().width() < changelog_width) + QTextBrowser* changelog_browser = new QTextBrowser(&mb); + changelog_browser->setOpenExternalLinks(true); + changelog_browser->setReadOnly(true); + changelog_browser->setFrameShape(QFrame::NoFrame); + changelog_browser->setLineWrapMode(QTextBrowser::NoWrap); + changelog_browser->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + changelog_browser->setHtml(QStringLiteral("

%0

%1").arg(tr("Changelog:"), changelog_html)); + + // Natural height for ≤6 entries, capped at 6-entry height for more + int browser_height; + + if (changelog_count > 6) { - update_message += " "; + changelog_browser->setHtml(QStringLiteral("

%0

%1").arg(tr("Changelog:"), changelog_html_6)); + browser_height = static_cast(changelog_browser->document()->size().height()); + changelog_browser->setHtml(QStringLiteral("

%0

%1").arg(tr("Changelog:"), changelog_html)); } + else + { + browser_height = static_cast(changelog_browser->document()->size().height()); + } + + changelog_browser->setFixedHeight(browser_height); + changelog_browser->setVisible(false); + + const QString show_text = tr("Show Changelog"); + const QString hide_text = tr("Hide Changelog"); + + QPushButton* toggle_btn = new QPushButton(show_text, &mb); + grid->addWidget(toggle_btn, row++, 0, 1, cols); + grid->addWidget(changelog_browser, row++, 0, 1, cols); + + QObject::connect(toggle_btn, &QPushButton::clicked, [changelog_browser, toggle_btn, &mb, show_text, hide_text]() + { + const bool becoming_visible = !changelog_browser->isVisible(); + changelog_browser->setVisible(becoming_visible); + toggle_btn->setText(becoming_visible ? hide_text : show_text); + mb.adjustSize(); + }); } + // Horizontal separator before the prompt + QFrame* separator = new QFrame(&mb); + separator->setFrameShape(QFrame::HLine); + separator->setFrameShadow(QFrame::Sunken); + grid->addWidget(separator, row++, 0, 1, cols); + + // "Do you want to update?" label + const QString prompt_text = m_update_info.hash_found + ? tr("Do you want to update?") + : tr("Do you want to update to the latest official RPCS3 version?"); + QLabel* prompt_label = new QLabel(prompt_text, &mb); + grid->addWidget(prompt_label, row++, 0, 1, cols); + + // Re-add button box at the bottom + if (button_box) + grid->addWidget(button_box, row, 0, 1, cols); + } + + // Pad message text to match changelog width + if (!changelog_html.isEmpty()) + { + const int target_width = 500; + while (QLabel(update_message).sizeHint().width() < target_width) + { + update_message += QStringLiteral(" "); + } mb.setText(update_message); } diff --git a/rpcs3/rpcs3qt/update_manager.h b/rpcs3/rpcs3qt/update_manager.h index 98ef5cf3f1..d03d2c5a59 100644 --- a/rpcs3/rpcs3qt/update_manager.h +++ b/rpcs3/rpcs3qt/update_manager.h @@ -29,6 +29,7 @@ private: struct changelog_data { + int pr = 0; QString version; QString title; };