mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-01 05:38:50 +00:00
Merge branch 'master' of https://github.com/dolphin-emu/dolphin into dolphin-emu-master
This commit is contained in:
commit
7e6752e8fc
516 changed files with 60670 additions and 270317 deletions
|
@ -89,8 +89,7 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent)
|
|||
// in your translation, please use the type of curly quotes that's appropriate for
|
||||
// your language. If you aren't sure which type is appropriate, see
|
||||
// https://en.wikipedia.org/wiki/Quotation_mark#Specific_language_features
|
||||
tr("\u00A9 2003-2015+ Dolphin Team. "
|
||||
"\u201cGameCube\u201d and \u201cWii\u201d are "
|
||||
tr("\u00A9 2003-2024+ Dolphin Team. \u201cGameCube\u201d and \u201cWii\u201d are "
|
||||
"trademarks of Nintendo. Dolphin is not affiliated with Nintendo in any way.")));
|
||||
|
||||
QLabel* logo = new QLabel();
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
#include "DolphinQt/Achievements/AchievementBox.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QProgressBar>
|
||||
#include <QSizePolicy>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
|
@ -18,6 +20,8 @@
|
|||
|
||||
#include "DolphinQt/QtUtils/FromStdString.h"
|
||||
|
||||
static constexpr size_t PROGRESS_LENGTH = 24;
|
||||
|
||||
AchievementBox::AchievementBox(QWidget* parent, rc_client_achievement_t* achievement)
|
||||
: QGroupBox(parent), m_achievement(achievement)
|
||||
{
|
||||
|
@ -27,23 +31,39 @@ AchievementBox::AchievementBox(QWidget* parent, rc_client_achievement_t* achieve
|
|||
|
||||
m_badge = new QLabel();
|
||||
QLabel* title = new QLabel(QString::fromUtf8(achievement->title, strlen(achievement->title)));
|
||||
title->setWordWrap(true);
|
||||
title->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
QLabel* description =
|
||||
new QLabel(QString::fromUtf8(achievement->description, strlen(achievement->description)));
|
||||
description->setWordWrap(true);
|
||||
description->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
QLabel* points = new QLabel(tr("%1 points").arg(achievement->points));
|
||||
m_status = new QLabel();
|
||||
m_progress_bar = new QProgressBar();
|
||||
QSizePolicy sp_retain = m_progress_bar->sizePolicy();
|
||||
sp_retain.setRetainSizeWhenHidden(true);
|
||||
m_progress_bar->setSizePolicy(sp_retain);
|
||||
m_progress_label = new QLabel();
|
||||
m_progress_label->setStyleSheet(QStringLiteral("background-color:transparent;"));
|
||||
m_progress_label->setAlignment(Qt::AlignCenter);
|
||||
|
||||
QVBoxLayout* a_col_left = new QVBoxLayout();
|
||||
a_col_left->addSpacerItem(new QSpacerItem(0, 0));
|
||||
a_col_left->addWidget(m_badge);
|
||||
a_col_left->addSpacerItem(new QSpacerItem(0, 0));
|
||||
a_col_left->setSizeConstraint(QLayout::SetFixedSize);
|
||||
a_col_left->setAlignment(Qt::AlignCenter);
|
||||
QVBoxLayout* a_col_right = new QVBoxLayout();
|
||||
a_col_right->addWidget(title);
|
||||
a_col_right->addWidget(description);
|
||||
a_col_right->addWidget(points);
|
||||
a_col_right->addWidget(m_status);
|
||||
a_col_right->addWidget(m_progress_bar);
|
||||
QVBoxLayout* a_prog_layout = new QVBoxLayout(m_progress_bar);
|
||||
a_prog_layout->setContentsMargins(0, 0, 0, 0);
|
||||
a_prog_layout->addWidget(m_progress_label);
|
||||
QHBoxLayout* a_total = new QHBoxLayout();
|
||||
a_total->addWidget(m_badge);
|
||||
a_total->addLayout(a_col_left);
|
||||
a_total->addLayout(a_col_right);
|
||||
setLayout(a_total);
|
||||
|
||||
|
@ -52,47 +72,65 @@ AchievementBox::AchievementBox(QWidget* parent, rc_client_achievement_t* achieve
|
|||
|
||||
void AchievementBox::UpdateData()
|
||||
{
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
|
||||
const auto& badge = AchievementManager::GetInstance().GetAchievementBadge(
|
||||
m_achievement->id, m_achievement->state != RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED);
|
||||
std::string_view color = AchievementManager::GRAY;
|
||||
if (m_achievement->unlocked & RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE)
|
||||
color = AchievementManager::GOLD;
|
||||
else if (m_achievement->unlocked & RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE)
|
||||
color = AchievementManager::BLUE;
|
||||
if (Config::Get(Config::RA_BADGES_ENABLED) && badge.name != "")
|
||||
{
|
||||
QImage i_badge{};
|
||||
if (i_badge.loadFromData(&badge.badge.front(), static_cast<int>(badge.badge.size())))
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
// rc_client guarantees m_achievement will be valid as long as the game is loaded
|
||||
if (!AchievementManager::GetInstance().IsGameLoaded())
|
||||
return;
|
||||
|
||||
const auto& badge = AchievementManager::GetInstance().GetAchievementBadge(
|
||||
m_achievement->id, !m_achievement->unlocked);
|
||||
std::string_view color = AchievementManager::GRAY;
|
||||
if (m_achievement->unlocked & RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE)
|
||||
color = AchievementManager::GOLD;
|
||||
else if (m_achievement->unlocked & RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE)
|
||||
color = AchievementManager::BLUE;
|
||||
QImage i_badge(&badge.data.front(), badge.width, badge.height, QImage::Format_RGBA8888);
|
||||
m_badge->setPixmap(
|
||||
QPixmap::fromImage(i_badge).scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
m_badge->adjustSize();
|
||||
m_badge->setStyleSheet(
|
||||
QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color)));
|
||||
|
||||
if (m_achievement->unlocked)
|
||||
{
|
||||
m_badge->setPixmap(QPixmap::fromImage(i_badge).scaled(64, 64, Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
m_badge->adjustSize();
|
||||
m_badge->setStyleSheet(
|
||||
QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color)));
|
||||
if (m_achievement->unlock_time != 0)
|
||||
{
|
||||
m_status->setText(
|
||||
// i18n: %1 is a date/time.
|
||||
tr("Unlocked at %1")
|
||||
.arg(QDateTime::fromSecsSinceEpoch(m_achievement->unlock_time).toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status->setText(tr("Unlocked"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status->setText(tr("Locked"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_badge->setText({});
|
||||
}
|
||||
|
||||
if (m_achievement->state == RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED)
|
||||
{
|
||||
m_status->setText(
|
||||
tr("Unlocked at %1")
|
||||
.arg(QDateTime::fromSecsSinceEpoch(m_achievement->unlock_time).toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status->setText(tr("Locked"));
|
||||
}
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
void AchievementBox::UpdateProgress()
|
||||
{
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
// rc_client guarantees m_achievement will be valid as long as the game is loaded
|
||||
if (!AchievementManager::GetInstance().IsGameLoaded())
|
||||
return;
|
||||
|
||||
if (m_achievement->measured_percent > 0.000)
|
||||
{
|
||||
m_progress_bar->setRange(0, 100);
|
||||
m_progress_bar->setValue(m_achievement->measured_percent);
|
||||
m_progress_bar->setValue(m_achievement->unlocked ? 100 : m_achievement->measured_percent);
|
||||
m_progress_bar->setTextVisible(false);
|
||||
m_progress_label->setText(
|
||||
QString::fromUtf8(m_achievement->measured_progress,
|
||||
qstrnlen(m_achievement->measured_progress, PROGRESS_LENGTH)));
|
||||
m_progress_label->setVisible(!m_achievement->unlocked);
|
||||
m_progress_bar->setVisible(true);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -20,11 +20,13 @@ class AchievementBox final : public QGroupBox
|
|||
public:
|
||||
explicit AchievementBox(QWidget* parent, rc_client_achievement_t* achievement);
|
||||
void UpdateData();
|
||||
void UpdateProgress();
|
||||
|
||||
private:
|
||||
QLabel* m_badge;
|
||||
QLabel* m_status;
|
||||
QProgressBar* m_progress_bar;
|
||||
QLabel* m_progress_label;
|
||||
|
||||
rc_client_achievement_t* m_achievement;
|
||||
};
|
||||
|
|
|
@ -27,11 +27,18 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
|
|||
m_name = new QLabel();
|
||||
m_points = new QLabel();
|
||||
m_game_progress = new QProgressBar();
|
||||
m_progress_label = new QLabel();
|
||||
m_rich_presence = new QLabel();
|
||||
|
||||
m_name->setWordWrap(true);
|
||||
m_points->setWordWrap(true);
|
||||
m_rich_presence->setWordWrap(true);
|
||||
QSizePolicy sp_retain = m_game_progress->sizePolicy();
|
||||
sp_retain.setRetainSizeWhenHidden(true);
|
||||
m_game_progress->setSizePolicy(sp_retain);
|
||||
m_game_progress->setTextVisible(false);
|
||||
m_progress_label->setStyleSheet(QStringLiteral("background-color:transparent;"));
|
||||
m_progress_label->setAlignment(Qt::AlignCenter);
|
||||
|
||||
QVBoxLayout* icon_col = new QVBoxLayout();
|
||||
icon_col->addWidget(m_user_icon);
|
||||
|
@ -41,6 +48,9 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
|
|||
text_col->addWidget(m_points);
|
||||
text_col->addWidget(m_game_progress);
|
||||
text_col->addWidget(m_rich_presence);
|
||||
QVBoxLayout* prog_layout = new QVBoxLayout(m_game_progress);
|
||||
prog_layout->setContentsMargins(0, 0, 0, 0);
|
||||
prog_layout->addWidget(m_progress_label);
|
||||
QHBoxLayout* header_layout = new QHBoxLayout();
|
||||
header_layout->addLayout(icon_col);
|
||||
header_layout->addLayout(text_col);
|
||||
|
@ -68,24 +78,23 @@ void AchievementHeaderWidget::UpdateData()
|
|||
|
||||
QString user_name = QtUtils::FromStdString(instance.GetPlayerDisplayName());
|
||||
QString game_name = QtUtils::FromStdString(instance.GetGameDisplayName());
|
||||
AchievementManager::BadgeStatus player_badge = instance.GetPlayerBadge();
|
||||
AchievementManager::BadgeStatus game_badge = instance.GetGameBadge();
|
||||
const AchievementManager::Badge& player_badge = instance.GetPlayerBadge();
|
||||
const AchievementManager::Badge& game_badge = instance.GetGameBadge();
|
||||
|
||||
m_user_icon->setVisible(false);
|
||||
m_user_icon->clear();
|
||||
m_user_icon->setText({});
|
||||
if (Config::Get(Config::RA_BADGES_ENABLED) && !player_badge.name.empty())
|
||||
if (!player_badge.data.empty())
|
||||
{
|
||||
QImage i_user_icon{};
|
||||
if (i_user_icon.loadFromData(&player_badge.badge.front(), (int)player_badge.badge.size()))
|
||||
{
|
||||
m_user_icon->setPixmap(QPixmap::fromImage(i_user_icon)
|
||||
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
m_user_icon->adjustSize();
|
||||
m_user_icon->setStyleSheet(QStringLiteral("border: 4px solid transparent"));
|
||||
m_user_icon->setVisible(true);
|
||||
}
|
||||
QImage i_user_icon(player_badge.data.data(), player_badge.width, player_badge.height,
|
||||
QImage::Format_RGBA8888);
|
||||
m_user_icon->setPixmap(QPixmap::fromImage(i_user_icon)
|
||||
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
m_user_icon->adjustSize();
|
||||
m_user_icon->setStyleSheet(QStringLiteral("border: 4px solid transparent"));
|
||||
m_user_icon->setVisible(true);
|
||||
|
||||
m_game_icon->setVisible(false);
|
||||
m_game_icon->clear();
|
||||
m_game_icon->setText({});
|
||||
|
@ -94,26 +103,22 @@ void AchievementHeaderWidget::UpdateData()
|
|||
{
|
||||
rc_client_user_game_summary_t game_summary;
|
||||
rc_client_get_user_game_summary(instance.GetClient(), &game_summary);
|
||||
|
||||
if (Config::Get(Config::RA_BADGES_ENABLED) && !game_badge.name.empty())
|
||||
if (!game_badge.data.empty())
|
||||
{
|
||||
QImage i_game_icon{};
|
||||
if (i_game_icon.loadFromData(&game_badge.badge.front(), (int)game_badge.badge.size()))
|
||||
{
|
||||
m_game_icon->setPixmap(QPixmap::fromImage(i_game_icon)
|
||||
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
m_game_icon->adjustSize();
|
||||
std::string_view color = AchievementManager::GRAY;
|
||||
if (game_summary.num_core_achievements == game_summary.num_unlocked_achievements)
|
||||
{
|
||||
color =
|
||||
instance.IsHardcoreModeActive() ? AchievementManager::GOLD : AchievementManager::BLUE;
|
||||
}
|
||||
m_game_icon->setStyleSheet(
|
||||
QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color)));
|
||||
m_game_icon->setVisible(true);
|
||||
}
|
||||
QImage i_game_icon(game_badge.data.data(), game_badge.width, game_badge.height,
|
||||
QImage::Format_RGBA8888);
|
||||
m_game_icon->setPixmap(QPixmap::fromImage(i_game_icon)
|
||||
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
m_game_icon->adjustSize();
|
||||
std::string_view color = AchievementManager::GRAY;
|
||||
if (game_summary.num_core_achievements == game_summary.num_unlocked_achievements)
|
||||
{
|
||||
color = instance.IsHardcoreModeActive() ? AchievementManager::GOLD : AchievementManager::BLUE;
|
||||
}
|
||||
m_game_icon->setStyleSheet(
|
||||
QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color)));
|
||||
m_game_icon->setVisible(true);
|
||||
|
||||
m_name->setText(tr("%1 is playing %2").arg(user_name).arg(game_name));
|
||||
m_points->setText(tr("%1 has unlocked %2/%3 achievements worth %4/%5 points")
|
||||
|
@ -123,10 +128,15 @@ void AchievementHeaderWidget::UpdateData()
|
|||
.arg(game_summary.points_unlocked)
|
||||
.arg(game_summary.points_core));
|
||||
|
||||
m_game_progress->setRange(0, game_summary.num_core_achievements);
|
||||
if (!m_game_progress->isVisible())
|
||||
m_game_progress->setVisible(true);
|
||||
// This ensures that 0/0 renders as empty instead of full
|
||||
m_game_progress->setRange(
|
||||
0, (game_summary.num_core_achievements == 0) ? 1 : game_summary.num_core_achievements);
|
||||
m_game_progress->setVisible(true);
|
||||
m_game_progress->setValue(game_summary.num_unlocked_achievements);
|
||||
m_progress_label->setVisible(true);
|
||||
m_progress_label->setText(tr("%1/%2")
|
||||
.arg(game_summary.num_unlocked_achievements)
|
||||
.arg(game_summary.num_core_achievements));
|
||||
m_rich_presence->setText(QString::fromUtf8(instance.GetRichPresence().data()));
|
||||
m_rich_presence->setVisible(true);
|
||||
}
|
||||
|
@ -136,6 +146,7 @@ void AchievementHeaderWidget::UpdateData()
|
|||
m_points->setText(tr("%1 points").arg(instance.GetPlayerScore()));
|
||||
|
||||
m_game_progress->setVisible(false);
|
||||
m_progress_label->setVisible(false);
|
||||
m_rich_presence->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ private:
|
|||
QLabel* m_name;
|
||||
QLabel* m_points;
|
||||
QProgressBar* m_game_progress;
|
||||
QLabel* m_progress_label;
|
||||
QLabel* m_rich_presence;
|
||||
QGroupBox* m_header_box;
|
||||
};
|
||||
|
|
|
@ -30,6 +30,7 @@ AchievementLeaderboardWidget::AchievementLeaderboardWidget(QWidget* parent) : QW
|
|||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
layout->addWidget(m_common_box);
|
||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
|
@ -44,39 +45,39 @@ void AchievementLeaderboardWidget::UpdateData(bool clean_all)
|
|||
return;
|
||||
auto* client = instance.GetClient();
|
||||
auto* leaderboard_list =
|
||||
rc_client_create_leaderboard_list(client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE);
|
||||
rc_client_create_leaderboard_list(client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_TRACKING);
|
||||
|
||||
u32 row = 0;
|
||||
for (u32 bucket = 0; bucket < leaderboard_list->num_buckets; bucket++)
|
||||
{
|
||||
const auto& leaderboard_bucket = leaderboard_list->buckets[bucket];
|
||||
m_common_layout->addWidget(new QLabel(tr(leaderboard_bucket.label)), row, 0);
|
||||
row += 2;
|
||||
for (u32 board = 0; board < leaderboard_bucket.num_leaderboards; board++)
|
||||
{
|
||||
const auto* leaderboard = leaderboard_bucket.leaderboards[board];
|
||||
m_leaderboard_order[leaderboard->id] = row;
|
||||
QLabel* a_title = new QLabel(QString::fromUtf8(leaderboard->title));
|
||||
a_title->setWordWrap(true);
|
||||
a_title->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
QLabel* a_description = new QLabel(QString::fromUtf8(leaderboard->description));
|
||||
a_description->setWordWrap(true);
|
||||
a_description->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
QVBoxLayout* a_col_left = new QVBoxLayout();
|
||||
a_col_left->addWidget(a_title);
|
||||
a_col_left->addWidget(a_description);
|
||||
if (row > 0)
|
||||
{
|
||||
QFrame* a_divider = new QFrame();
|
||||
a_divider->setFrameShape(QFrame::HLine);
|
||||
m_common_layout->addWidget(a_divider, row - 1, 0);
|
||||
}
|
||||
QFrame* a_divider = new QFrame();
|
||||
a_divider->setFrameShape(QFrame::HLine);
|
||||
m_common_layout->addWidget(a_divider, row - 1, 0);
|
||||
m_common_layout->addLayout(a_col_left, row, 0);
|
||||
for (size_t ix = 0; ix < 4; ix++)
|
||||
{
|
||||
QVBoxLayout* a_col = new QVBoxLayout();
|
||||
for (size_t jx = 0; jx < 3; jx++)
|
||||
a_col->addWidget(new QLabel(QStringLiteral("---")));
|
||||
if (row > 0)
|
||||
{
|
||||
QFrame* a_divider = new QFrame();
|
||||
a_divider->setFrameShape(QFrame::HLine);
|
||||
m_common_layout->addWidget(a_divider, row - 1, static_cast<int>(ix) + 1);
|
||||
}
|
||||
QFrame* a_divider_2 = new QFrame();
|
||||
a_divider_2->setFrameShape(QFrame::HLine);
|
||||
m_common_layout->addWidget(a_divider_2, row - 1, static_cast<int>(ix) + 1);
|
||||
m_common_layout->addLayout(a_col, row, static_cast<int>(ix) + 1);
|
||||
}
|
||||
row += 2;
|
||||
|
@ -86,7 +87,7 @@ void AchievementLeaderboardWidget::UpdateData(bool clean_all)
|
|||
}
|
||||
for (auto row : m_leaderboard_order)
|
||||
{
|
||||
UpdateRow(row.second);
|
||||
UpdateRow(row.first);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +98,7 @@ void AchievementLeaderboardWidget::UpdateData(
|
|||
{
|
||||
if (update_ids.contains(row.first))
|
||||
{
|
||||
UpdateRow(row.second);
|
||||
UpdateRow(row.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ AchievementProgressWidget::AchievementProgressWidget(QWidget* parent) : QWidget(
|
|||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
layout->addWidget(m_common_box);
|
||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
|
@ -42,32 +43,51 @@ void AchievementProgressWidget::UpdateData(bool clean_all)
|
|||
{
|
||||
m_achievement_boxes.clear();
|
||||
ClearLayoutRecursively(m_common_layout);
|
||||
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
if (!instance.IsGameLoaded())
|
||||
return;
|
||||
auto* client = instance.GetClient();
|
||||
auto* achievement_list = rc_client_create_achievement_list(
|
||||
client, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL,
|
||||
RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_LOCK_STATE);
|
||||
for (u32 ix = 0; ix < achievement_list->num_buckets; ix++)
|
||||
{
|
||||
for (u32 jx = 0; jx < achievement_list->buckets[ix].num_achievements; jx++)
|
||||
{
|
||||
auto* achievement = achievement_list->buckets[ix].achievements[jx];
|
||||
m_achievement_boxes[achievement->id] = std::make_shared<AchievementBox>(this, achievement);
|
||||
m_common_layout->addWidget(m_achievement_boxes[achievement->id].get());
|
||||
}
|
||||
}
|
||||
rc_client_destroy_achievement_list(achievement_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto box : m_achievement_boxes)
|
||||
while (auto* item = m_common_layout->takeAt(0))
|
||||
{
|
||||
box.second->UpdateData();
|
||||
auto* widget = item->widget();
|
||||
m_common_layout->removeWidget(widget);
|
||||
if (std::strcmp(widget->metaObject()->className(), "QLabel") == 0)
|
||||
{
|
||||
widget->deleteLater();
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
if (!instance.IsGameLoaded())
|
||||
return;
|
||||
auto* client = instance.GetClient();
|
||||
auto* achievement_list =
|
||||
rc_client_create_achievement_list(client, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL,
|
||||
RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_PROGRESS);
|
||||
if (!achievement_list)
|
||||
return;
|
||||
for (u32 ix = 0; ix < achievement_list->num_buckets; ix++)
|
||||
{
|
||||
m_common_layout->addWidget(new QLabel(tr(achievement_list->buckets[ix].label)));
|
||||
for (u32 jx = 0; jx < achievement_list->buckets[ix].num_achievements; jx++)
|
||||
{
|
||||
auto* achievement = achievement_list->buckets[ix].achievements[jx];
|
||||
auto box_itr = m_achievement_boxes.lower_bound(achievement->id);
|
||||
if (box_itr != m_achievement_boxes.end() && box_itr->first == achievement->id)
|
||||
{
|
||||
box_itr->second->UpdateProgress();
|
||||
m_common_layout->addWidget(box_itr->second.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto new_box_itr = m_achievement_boxes.try_emplace(
|
||||
box_itr, achievement->id, std::make_shared<AchievementBox>(this, achievement));
|
||||
m_common_layout->addWidget(new_box_itr->second.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
rc_client_destroy_achievement_list(achievement_list);
|
||||
}
|
||||
|
||||
void AchievementProgressWidget::UpdateData(
|
||||
|
|
|
@ -105,11 +105,6 @@ void AchievementSettingsWidget::CreateLayout()
|
|||
tr("Enable progress notifications on achievements.<br><br>Displays a brief popup message "
|
||||
"whenever the player makes progress on an achievement that tracks an accumulated value, "
|
||||
"such as 60 out of 120 stars."));
|
||||
m_common_badges_enabled_input = new ToolTipCheckBox(tr("Enable Achievement Badges"));
|
||||
m_common_badges_enabled_input->SetDescription(
|
||||
tr("Enable achievement badges.<br><br>Displays icons for the player, game, and achievements. "
|
||||
"Simple visual option, but will require a small amount of extra memory and time to "
|
||||
"download the images."));
|
||||
|
||||
m_common_layout->addWidget(m_common_integration_enabled_input);
|
||||
m_common_layout->addWidget(m_common_username_label);
|
||||
|
@ -119,17 +114,18 @@ void AchievementSettingsWidget::CreateLayout()
|
|||
m_common_layout->addWidget(m_common_login_button);
|
||||
m_common_layout->addWidget(m_common_logout_button);
|
||||
m_common_layout->addWidget(m_common_login_failed);
|
||||
// i18n: Settings that affect the functionality of unlocking achievements.
|
||||
m_common_layout->addWidget(new QLabel(tr("Function Settings")));
|
||||
m_common_layout->addWidget(m_common_hardcore_enabled_input);
|
||||
m_common_layout->addWidget(m_common_unofficial_enabled_input);
|
||||
m_common_layout->addWidget(m_common_encore_enabled_input);
|
||||
m_common_layout->addWidget(m_common_spectator_enabled_input);
|
||||
// i18n: Settings that affect how achievements are displayed while playing.
|
||||
m_common_layout->addWidget(new QLabel(tr("Display Settings")));
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
m_common_layout->addWidget(m_common_discord_presence_enabled_input);
|
||||
#endif // USE_DISCORD_PRESENCE
|
||||
m_common_layout->addWidget(m_common_progress_enabled_input);
|
||||
m_common_layout->addWidget(m_common_badges_enabled_input);
|
||||
|
||||
m_common_layout->setAlignment(Qt::AlignTop);
|
||||
setLayout(m_common_layout);
|
||||
|
@ -139,8 +135,8 @@ void AchievementSettingsWidget::ConnectWidgets()
|
|||
{
|
||||
connect(m_common_integration_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleRAIntegration);
|
||||
connect(m_common_login_button, &QPushButton::pressed, this, &AchievementSettingsWidget::Login);
|
||||
connect(m_common_logout_button, &QPushButton::pressed, this, &AchievementSettingsWidget::Logout);
|
||||
connect(m_common_login_button, &QPushButton::clicked, this, &AchievementSettingsWidget::Login);
|
||||
connect(m_common_logout_button, &QPushButton::clicked, this, &AchievementSettingsWidget::Logout);
|
||||
connect(m_common_hardcore_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleHardcore);
|
||||
connect(m_common_unofficial_enabled_input, &QCheckBox::toggled, this,
|
||||
|
@ -153,8 +149,6 @@ void AchievementSettingsWidget::ConnectWidgets()
|
|||
&AchievementSettingsWidget::ToggleDiscordPresence);
|
||||
connect(m_common_progress_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleProgress);
|
||||
connect(m_common_badges_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleBadges);
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::OnControllerInterfaceConfigure()
|
||||
|
@ -184,6 +178,15 @@ void AchievementSettingsWidget::LoadSettings()
|
|||
SignalBlocking(m_common_login_button)->setVisible(logged_out);
|
||||
SignalBlocking(m_common_login_button)
|
||||
->setEnabled(enabled && !Core::IsRunning(Core::System::GetInstance()));
|
||||
if (enabled && Core::IsRunning(Core::System::GetInstance()))
|
||||
{
|
||||
SignalBlocking(m_common_login_button)->setText(tr("To log in, stop the current emulation."));
|
||||
}
|
||||
else
|
||||
{
|
||||
SignalBlocking(m_common_login_button)->setText(tr("Log In"));
|
||||
}
|
||||
|
||||
SignalBlocking(m_common_logout_button)->setVisible(!logged_out);
|
||||
SignalBlocking(m_common_logout_button)->setEnabled(enabled);
|
||||
|
||||
|
@ -214,9 +217,6 @@ void AchievementSettingsWidget::LoadSettings()
|
|||
SignalBlocking(m_common_progress_enabled_input)
|
||||
->setChecked(Config::Get(Config::RA_PROGRESS_ENABLED));
|
||||
SignalBlocking(m_common_progress_enabled_input)->setEnabled(enabled);
|
||||
|
||||
SignalBlocking(m_common_badges_enabled_input)->setChecked(Config::Get(Config::RA_BADGES_ENABLED));
|
||||
SignalBlocking(m_common_badges_enabled_input)->setEnabled(enabled);
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::SaveSettings()
|
||||
|
@ -235,7 +235,6 @@ void AchievementSettingsWidget::SaveSettings()
|
|||
m_common_discord_presence_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_PROGRESS_ENABLED,
|
||||
m_common_progress_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_BADGES_ENABLED, m_common_badges_enabled_input->isChecked());
|
||||
Config::Save();
|
||||
}
|
||||
|
||||
|
@ -249,7 +248,10 @@ void AchievementSettingsWidget::ToggleRAIntegration()
|
|||
else
|
||||
instance.Shutdown();
|
||||
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
||||
{
|
||||
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance()));
|
||||
emit Settings::Instance().HardcoreStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::Login()
|
||||
|
@ -275,10 +277,11 @@ void AchievementSettingsWidget::ToggleHardcore()
|
|||
if (Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f)
|
||||
Config::SetBaseOrCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
|
||||
Config::SetBaseOrCurrent(Config::FREE_LOOK_ENABLED, false);
|
||||
Settings::Instance().SetCheatsEnabled(false);
|
||||
Config::SetBaseOrCurrent(Config::MAIN_ENABLE_CHEATS, false);
|
||||
Settings::Instance().SetDebugModeEnabled(false);
|
||||
}
|
||||
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance()));
|
||||
emit Settings::Instance().HardcoreStateChanged();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleUnofficial()
|
||||
|
@ -308,11 +311,4 @@ void AchievementSettingsWidget::ToggleProgress()
|
|||
SaveSettings();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleBadges()
|
||||
{
|
||||
SaveSettings();
|
||||
AchievementManager::GetInstance().FetchPlayerBadge();
|
||||
AchievementManager::GetInstance().FetchGameBadges();
|
||||
}
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -38,7 +38,6 @@ private:
|
|||
void ToggleSpectator();
|
||||
void ToggleDiscordPresence();
|
||||
void ToggleProgress();
|
||||
void ToggleBadges();
|
||||
|
||||
QGroupBox* m_common_box;
|
||||
QVBoxLayout* m_common_layout;
|
||||
|
@ -56,7 +55,6 @@ private:
|
|||
ToolTipCheckBox* m_common_spectator_enabled_input;
|
||||
ToolTipCheckBox* m_common_discord_presence_enabled_input;
|
||||
ToolTipCheckBox* m_common_progress_enabled_input;
|
||||
ToolTipCheckBox* m_common_badges_enabled_input;
|
||||
};
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <mutex>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QTabWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
|
@ -34,6 +36,8 @@ AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
|
|||
});
|
||||
});
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
||||
[this] { m_settings_widget->UpdateData(); });
|
||||
connect(&Settings::Instance(), &Settings::HardcoreStateChanged, this,
|
||||
[this] { AchievementsWindow::UpdateData({.all = true}); });
|
||||
}
|
||||
|
||||
|
@ -81,6 +85,8 @@ void AchievementsWindow::UpdateData(AchievementManager::UpdatedItems updated_ite
|
|||
m_header_widget->UpdateData();
|
||||
m_progress_widget->UpdateData(true);
|
||||
m_leaderboard_widget->UpdateData(true);
|
||||
static_cast<QScrollArea*>(m_tab_widget->widget(1))->verticalScrollBar()->setValue(0);
|
||||
static_cast<QScrollArea*>(m_tab_widget->widget(2))->verticalScrollBar()->setValue(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -89,7 +95,7 @@ void AchievementsWindow::UpdateData(AchievementManager::UpdatedItems updated_ite
|
|||
{
|
||||
m_header_widget->UpdateData();
|
||||
}
|
||||
if (updated_items.all_achievements)
|
||||
if (updated_items.all_achievements || updated_items.rich_presence)
|
||||
m_progress_widget->UpdateData(false);
|
||||
else if (updated_items.achievements.size() > 0)
|
||||
m_progress_widget->UpdateData(updated_items.achievements);
|
||||
|
|
|
@ -566,15 +566,14 @@ endif()
|
|||
|
||||
if(APPLE)
|
||||
include(BundleUtilities)
|
||||
set(BUNDLE_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Dolphin.app)
|
||||
set(BUNDLE_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/DolphinQt.app)
|
||||
|
||||
# Ask for an application bundle.
|
||||
set_target_properties(dolphin-mpn PROPERTIES
|
||||
MACOSX_BUNDLE true
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/DolphinEmu.entitlements"
|
||||
XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--deep --options=runtime"
|
||||
OUTPUT_NAME Dolphin
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/Info.plist"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
|
||||
OUTPUT_NAME DolphinQt
|
||||
)
|
||||
|
||||
# Copy qt.conf into the bundle
|
||||
|
@ -638,56 +637,18 @@ if(APPLE)
|
|||
endif()
|
||||
|
||||
if(MACOS_CODE_SIGNING)
|
||||
# Code sign make file builds
|
||||
add_custom_command(TARGET dolphin-mpn POST_BUILD
|
||||
COMMAND /usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY}" --deep --options=runtime --entitlements "${CMAKE_SOURCE_DIR}/Source/Core/DolphinQt/DolphinEmu$<$<CONFIG:Debug>:Debug>.entitlements" "$<TARGET_BUNDLE_DIR:dolphin-mpn>")
|
||||
add_custom_command(TARGET dolphin-mpn
|
||||
POST_BUILD
|
||||
COMMAND "${CMAKE_SOURCE_DIR}/Tools/mac-codesign.sh"
|
||||
"-e" "${CMAKE_CURRENT_SOURCE_DIR}/DolphinEmu$<$<CONFIG:Debug>:Debug>.entitlements"
|
||||
"${MACOS_CODE_SIGNING_IDENTITY}"
|
||||
"${BUNDLE_PATH}"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
install(TARGETS dolphin-mpn RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND STEAM)
|
||||
# Set that we want ORIGIN in FLAGS.
|
||||
# We also want RPATH, not RUNPATH, so disable the new tags.
|
||||
target_link_options(dolphin-mpn
|
||||
PRIVATE
|
||||
LINKER:-z,origin
|
||||
LINKER:--disable-new-dtags
|
||||
)
|
||||
|
||||
# For Steam Runtime builds, our Qt shared libraries will be in a "lib" folder.
|
||||
set_target_properties(dolphin-mpn PROPERTIES
|
||||
BUILD_WITH_INSTALL_RPATH true
|
||||
INSTALL_RPATH "\$ORIGIN/lib"
|
||||
)
|
||||
|
||||
add_custom_command(TARGET dolphin-mpn POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib"
|
||||
COMMAND cp "${Qt6_DIR}/../../LICENSE.*" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib"
|
||||
COMMAND cp -P "${Qt6_DIR}/../../*.so*" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${Qt6_DIR}/../../../plugins" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/plugins"
|
||||
)
|
||||
|
||||
# Copy qt.conf
|
||||
target_sources(dolphin-mpn PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/qt.conf")
|
||||
add_custom_command(TARGET dolphin-mpn POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/qt.conf" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/qt.conf"
|
||||
)
|
||||
|
||||
# Mark all data files as resources
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/Data/Sys")
|
||||
file(GLOB_RECURSE resources RELATIVE "${CMAKE_SOURCE_DIR}/Data" "${CMAKE_SOURCE_DIR}/Data/Sys/*")
|
||||
foreach(res ${resources})
|
||||
target_sources(dolphin-mpn PRIVATE "${CMAKE_SOURCE_DIR}/Data/${res}")
|
||||
source_group("Resources" FILES "${CMAKE_SOURCE_DIR}/Data/${res}")
|
||||
endforeach()
|
||||
|
||||
# Copy Sys folder
|
||||
add_custom_command(TARGET dolphin-mpn POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/Data/Sys" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(USE_MGBA)
|
||||
target_sources(dolphin-mpn PRIVATE
|
||||
GBAHost.cpp
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/CheatSearchFactoryWidget.h"
|
||||
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -26,6 +25,7 @@
|
|||
#include "Core/System.h"
|
||||
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
||||
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
|
||||
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
||||
|
||||
CheatSearchFactoryWidget::CheatSearchFactoryWidget()
|
||||
{
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/CheatSearchWidget.h"
|
||||
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
@ -42,6 +41,7 @@
|
|||
|
||||
#include "DolphinQt/Config/CheatCodeEditor.h"
|
||||
#include "DolphinQt/Config/CheatWarningWidget.h"
|
||||
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
#include "UICommon/GameFile.h"
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/Config/ARCodeWidget.h"
|
||||
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
@ -25,6 +24,7 @@
|
|||
#include "DolphinQt/Config/HardcoreWarningWidget.h"
|
||||
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
|
||||
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
|
||||
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
||||
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
|
|
|
@ -30,3 +30,60 @@ void ConfigChoice::Update(int choice)
|
|||
{
|
||||
Config::SetBaseOrCurrent(m_setting, choice);
|
||||
}
|
||||
|
||||
ConfigStringChoice::ConfigStringChoice(const std::vector<std::string>& options,
|
||||
const Config::Info<std::string>& setting)
|
||||
: m_setting(setting), m_text_is_data(true)
|
||||
{
|
||||
for (const auto& op : options)
|
||||
addItem(QString::fromStdString(op));
|
||||
|
||||
Connect();
|
||||
Load();
|
||||
}
|
||||
|
||||
ConfigStringChoice::ConfigStringChoice(const std::vector<std::pair<QString, QString>>& options,
|
||||
const Config::Info<std::string>& setting)
|
||||
: m_setting(setting), m_text_is_data(false)
|
||||
{
|
||||
for (const auto& [option_text, option_data] : options)
|
||||
addItem(option_text, option_data);
|
||||
|
||||
Connect();
|
||||
Load();
|
||||
}
|
||||
|
||||
void ConfigStringChoice::Connect()
|
||||
{
|
||||
const auto on_config_changed = [this]() {
|
||||
QFont bf = font();
|
||||
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
|
||||
setFont(bf);
|
||||
|
||||
Load();
|
||||
};
|
||||
|
||||
connect(&Settings::Instance(), &Settings::ConfigChanged, this, on_config_changed);
|
||||
connect(this, &QComboBox::currentIndexChanged, this, &ConfigStringChoice::Update);
|
||||
}
|
||||
|
||||
void ConfigStringChoice::Update(int index)
|
||||
{
|
||||
if (m_text_is_data)
|
||||
{
|
||||
Config::SetBaseOrCurrent(m_setting, itemText(index).toStdString());
|
||||
}
|
||||
else
|
||||
{
|
||||
Config::SetBaseOrCurrent(m_setting, itemData(index).toString().toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigStringChoice::Load()
|
||||
{
|
||||
const QString setting_value = QString::fromStdString(Config::Get(m_setting));
|
||||
|
||||
const int index = m_text_is_data ? findText(setting_value) : findData(setting_value);
|
||||
const QSignalBlocker blocker(this);
|
||||
setCurrentIndex(index);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h"
|
||||
|
||||
#include "Common/Config/Config.h"
|
||||
|
@ -18,3 +22,21 @@ private:
|
|||
|
||||
Config::Info<int> m_setting;
|
||||
};
|
||||
|
||||
class ConfigStringChoice : public ToolTipComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ConfigStringChoice(const std::vector<std::string>& options,
|
||||
const Config::Info<std::string>& setting);
|
||||
ConfigStringChoice(const std::vector<std::pair<QString, QString>>& options,
|
||||
const Config::Info<std::string>& setting);
|
||||
|
||||
private:
|
||||
void Connect();
|
||||
void Update(int index);
|
||||
void Load();
|
||||
|
||||
Config::Info<std::string> m_setting;
|
||||
bool m_text_is_data = false;
|
||||
};
|
||||
|
|
|
@ -27,8 +27,13 @@ ConfigRadioInt::ConfigRadioInt(const QString& label, const Config::Info<int>& se
|
|||
|
||||
void ConfigRadioInt::Update()
|
||||
{
|
||||
if (!isChecked())
|
||||
return;
|
||||
|
||||
Config::SetBaseOrCurrent(m_setting, m_value);
|
||||
if (isChecked())
|
||||
{
|
||||
Config::SetBaseOrCurrent(m_setting, m_value);
|
||||
emit OnSelected(m_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit OnDeselected(m_value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,12 @@ class ConfigRadioInt : public ToolTipRadioButton
|
|||
public:
|
||||
ConfigRadioInt(const QString& label, const Config::Info<int>& setting, int value);
|
||||
|
||||
signals:
|
||||
// Since selecting a new radio button deselects the old one, ::toggled will generate two signals.
|
||||
// These are convenience functions so you can receive only one signal if desired.
|
||||
void OnSelected(int new_value);
|
||||
void OnDeselected(int old_value);
|
||||
|
||||
private:
|
||||
void Update();
|
||||
|
||||
|
|
|
@ -213,8 +213,7 @@ void FilesystemWidget::PopulateDirectory(int partition_id, QStandardItem* root,
|
|||
|
||||
QString FilesystemWidget::SelectFolder()
|
||||
{
|
||||
return DolphinFileDialog::getExistingDirectory(this,
|
||||
QObject::tr("Choose the folder to extract to"));
|
||||
return DolphinFileDialog::getExistingDirectory(this, QObject::tr("Choose Folder to Extract To"));
|
||||
}
|
||||
|
||||
void FilesystemWidget::ShowContextMenu(const QPoint&)
|
||||
|
@ -267,7 +266,7 @@ void FilesystemWidget::ShowContextMenu(const QPoint&)
|
|||
switch (type)
|
||||
{
|
||||
case EntryType::Disc:
|
||||
menu->addAction(tr("Extract Entire Disc..."), this, [this, path] {
|
||||
menu->addAction(tr("Extract Entire Disc..."), this, [this] {
|
||||
auto folder = SelectFolder();
|
||||
|
||||
if (folder.isEmpty())
|
||||
|
@ -300,7 +299,7 @@ void FilesystemWidget::ShowContextMenu(const QPoint&)
|
|||
case EntryType::File:
|
||||
menu->addAction(tr("Extract File..."), this, [this, partition, path] {
|
||||
auto dest =
|
||||
DolphinFileDialog::getSaveFileName(this, tr("Save File to"), QFileInfo(path).fileName());
|
||||
DolphinFileDialog::getSaveFileName(this, tr("Save File To"), QFileInfo(path).fileName());
|
||||
|
||||
if (!dest.isEmpty())
|
||||
ExtractFile(partition, path, dest);
|
||||
|
|
|
@ -143,7 +143,7 @@ void GameConfigEdit::OnSelectionChanged()
|
|||
{
|
||||
const QString& keyword = m_edit->textCursor().selectedText();
|
||||
|
||||
if (m_keyword_map.count(keyword))
|
||||
if (m_keyword_map.contains(keyword))
|
||||
QWhatsThis::showText(QCursor::pos(), m_keyword_map[keyword], this);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/Config/GeckoCodeWidget.h"
|
||||
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
@ -31,6 +30,7 @@
|
|||
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
||||
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
|
||||
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
|
||||
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
||||
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
|
|
|
@ -241,7 +241,7 @@ void EnhancementsWidget::ConnectWidgets()
|
|||
connect(m_3d_mode, &QComboBox::currentIndexChanged, [this] {
|
||||
m_block_save = true;
|
||||
m_configure_color_correction->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
|
||||
LoadPPShaders();
|
||||
LoadPPShaders(static_cast<StereoMode>(m_3d_mode->currentIndex()));
|
||||
m_block_save = false;
|
||||
|
||||
SaveSettings();
|
||||
|
@ -250,23 +250,30 @@ void EnhancementsWidget::ConnectWidgets()
|
|||
&EnhancementsWidget::ConfigureColorCorrection);
|
||||
connect(m_configure_pp_effect, &QPushButton::clicked, this,
|
||||
&EnhancementsWidget::ConfigurePostProcessingShader);
|
||||
|
||||
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
|
||||
const QSignalBlocker blocker(this);
|
||||
m_block_save = true;
|
||||
LoadPPShaders(Config::Get(Config::GFX_STEREO_MODE));
|
||||
m_block_save = false;
|
||||
});
|
||||
}
|
||||
|
||||
void EnhancementsWidget::LoadPPShaders()
|
||||
void EnhancementsWidget::LoadPPShaders(StereoMode stereo_mode)
|
||||
{
|
||||
std::vector<std::string> shaders = VideoCommon::PostProcessing::GetShaderList();
|
||||
if (g_Config.stereo_mode == StereoMode::Anaglyph)
|
||||
if (stereo_mode == StereoMode::Anaglyph)
|
||||
{
|
||||
shaders = VideoCommon::PostProcessing::GetAnaglyphShaderList();
|
||||
}
|
||||
else if (g_Config.stereo_mode == StereoMode::Passive)
|
||||
else if (stereo_mode == StereoMode::Passive)
|
||||
{
|
||||
shaders = VideoCommon::PostProcessing::GetPassiveShaderList();
|
||||
}
|
||||
|
||||
m_pp_effect->clear();
|
||||
|
||||
if (g_Config.stereo_mode != StereoMode::Anaglyph && g_Config.stereo_mode != StereoMode::Passive)
|
||||
if (stereo_mode != StereoMode::Anaglyph && stereo_mode != StereoMode::Passive)
|
||||
m_pp_effect->addItem(tr("(off)"));
|
||||
|
||||
auto selected_shader = Config::Get(Config::GFX_ENHANCE_POST_SHADER);
|
||||
|
@ -283,10 +290,23 @@ void EnhancementsWidget::LoadPPShaders()
|
|||
}
|
||||
}
|
||||
|
||||
if (g_Config.stereo_mode == StereoMode::Anaglyph && !found)
|
||||
m_pp_effect->setCurrentIndex(m_pp_effect->findText(QStringLiteral("dubois")));
|
||||
else if (g_Config.stereo_mode == StereoMode::Passive && !found)
|
||||
m_pp_effect->setCurrentIndex(m_pp_effect->findText(QStringLiteral("horizontal")));
|
||||
if (!found)
|
||||
{
|
||||
if (stereo_mode == StereoMode::Anaglyph)
|
||||
selected_shader = "dubois";
|
||||
else if (stereo_mode == StereoMode::Passive)
|
||||
selected_shader = "horizontal";
|
||||
else
|
||||
selected_shader = "";
|
||||
|
||||
int index = m_pp_effect->findText(QString::fromStdString(selected_shader));
|
||||
if (index >= 0)
|
||||
m_pp_effect->setCurrentIndex(index);
|
||||
else
|
||||
m_pp_effect->setCurrentIndex(0);
|
||||
|
||||
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER, selected_shader);
|
||||
}
|
||||
|
||||
const bool supports_postprocessing = g_Config.backend_info.bSupportsPostProcessing;
|
||||
m_pp_effect->setEnabled(supports_postprocessing);
|
||||
|
@ -381,7 +401,7 @@ void EnhancementsWidget::LoadSettings()
|
|||
m_configure_color_correction->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
|
||||
|
||||
// Post Processing Shader
|
||||
LoadPPShaders();
|
||||
LoadPPShaders(Config::Get(Config::GFX_STEREO_MODE));
|
||||
|
||||
// Stereoscopy
|
||||
const bool supports_stereoscopy = g_Config.backend_info.bSupportsGeometryShaders;
|
||||
|
|
|
@ -17,6 +17,7 @@ class QPushButton;
|
|||
class QSlider;
|
||||
class ToolTipComboBox;
|
||||
class ToolTipPushButton;
|
||||
enum class StereoMode : int;
|
||||
|
||||
class EnhancementsWidget final : public QWidget
|
||||
{
|
||||
|
@ -33,7 +34,7 @@ private:
|
|||
void AddDescriptions();
|
||||
void ConfigureColorCorrection();
|
||||
void ConfigurePostProcessingShader();
|
||||
void LoadPPShaders();
|
||||
void LoadPPShaders(StereoMode stereo_mode);
|
||||
|
||||
// Enhancements
|
||||
ConfigChoice* m_ir_combo;
|
||||
|
|
|
@ -140,7 +140,7 @@ void GraphicsModListWidget::RefreshModList()
|
|||
// If no group matches the mod's features, or if the mod has no features, skip it
|
||||
if (std::none_of(mod.m_features.begin(), mod.m_features.end(),
|
||||
[&groups](const GraphicsModFeatureConfig& feature) {
|
||||
return groups.count(feature.m_group) == 1;
|
||||
return groups.contains(feature.m_group);
|
||||
}))
|
||||
{
|
||||
continue;
|
||||
|
|
|
@ -59,8 +59,6 @@
|
|||
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
||||
#include "InputCommon/InputConfig.h"
|
||||
|
||||
constexpr const char* PROFILES_DIR = "Profiles/";
|
||||
|
||||
MappingWindow::MappingWindow(QWidget* parent, Type type, int port_num)
|
||||
: QDialog(parent), m_port(port_num)
|
||||
{
|
||||
|
|
|
@ -177,10 +177,8 @@ void VerifyWidget::Verify()
|
|||
}
|
||||
verifier.Finish();
|
||||
|
||||
const DiscIO::VolumeVerifier::Result result = verifier.GetResult();
|
||||
progress.Reset();
|
||||
|
||||
return result;
|
||||
return verifier.GetResult();
|
||||
});
|
||||
SetQWidgetWindowDecorations(progress.GetRaw());
|
||||
progress.GetRaw()->exec();
|
||||
|
|
|
@ -367,7 +367,7 @@ void ConvertDialog::Convert()
|
|||
if (m_files.size() > 1)
|
||||
{
|
||||
dst_dir = DolphinFileDialog::getExistingDirectory(
|
||||
this, tr("Select where you want to save the converted images"),
|
||||
this, tr("Save Converted Images"),
|
||||
QFileInfo(QString::fromStdString(m_files[0]->GetFilePath())).dir().absolutePath());
|
||||
|
||||
if (dst_dir.isEmpty())
|
||||
|
@ -376,7 +376,7 @@ void ConvertDialog::Convert()
|
|||
else
|
||||
{
|
||||
dst_path = DolphinFileDialog::getSaveFileName(
|
||||
this, tr("Select where you want to save the converted image"),
|
||||
this, tr("Save Converted Image"),
|
||||
QFileInfo(QString::fromStdString(m_files[0]->GetFilePath()))
|
||||
.dir()
|
||||
.absoluteFilePath(
|
||||
|
|
|
@ -770,7 +770,7 @@ bool AssemblerWidget::SaveEditor(AsmEditor* editor)
|
|||
|
||||
QString selected_filter;
|
||||
save_path = DolphinFileDialog::getSaveFileName(
|
||||
this, tr("Save File to"), QString::fromStdString(default_dir),
|
||||
this, tr("Save File To"), QString::fromStdString(default_dir),
|
||||
QStringLiteral("%1;;%2").arg(asm_filter).arg(all_filter), &selected_filter);
|
||||
|
||||
if (save_path.isEmpty())
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
#include <QApplication>
|
||||
|
@ -46,6 +47,7 @@
|
|||
#include "DolphinQt/QtUtils/DolphinFileDialog.h"
|
||||
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
||||
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
|
||||
#include "DolphinQt/Resources.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
class BranchWatchProxyModel final : public QSortFilterProxyModel
|
||||
|
@ -57,6 +59,12 @@ public:
|
|||
: QSortFilterProxyModel(parent), m_branch_watch(branch_watch)
|
||||
{
|
||||
}
|
||||
~BranchWatchProxyModel() override = default;
|
||||
|
||||
BranchWatchProxyModel(const BranchWatchProxyModel&) = delete;
|
||||
BranchWatchProxyModel(BranchWatchProxyModel&&) = delete;
|
||||
BranchWatchProxyModel& operator=(const BranchWatchProxyModel&) = delete;
|
||||
BranchWatchProxyModel& operator=(BranchWatchProxyModel&&) = delete;
|
||||
|
||||
BranchWatchTableModel* sourceModel() const
|
||||
{
|
||||
|
@ -93,7 +101,6 @@ public:
|
|||
this->*member = std::nullopt;
|
||||
invalidateRowsFilter();
|
||||
}
|
||||
void OnDelete(QModelIndexList index_list);
|
||||
|
||||
bool IsBranchTypeAllowed(UGeckoInstruction inst) const;
|
||||
void SetInspected(const QModelIndex& index);
|
||||
|
@ -135,27 +142,20 @@ bool BranchWatchProxyModel::filterAcceptsRow(int source_row, const QModelIndex&)
|
|||
if (!m_origin_symbol_name.isEmpty())
|
||||
{
|
||||
if (const QVariant& symbol_name_v = sourceModel()->GetSymbolList()[source_row].origin_name;
|
||||
!symbol_name_v.isValid() ||
|
||||
!symbol_name_v.value<QString>().contains(m_origin_symbol_name, Qt::CaseInsensitive))
|
||||
!symbol_name_v.isValid() || !static_cast<const QString*>(symbol_name_v.data())
|
||||
->contains(m_origin_symbol_name, Qt::CaseInsensitive))
|
||||
return false;
|
||||
}
|
||||
if (!m_destin_symbol_name.isEmpty())
|
||||
{
|
||||
if (const QVariant& symbol_name_v = sourceModel()->GetSymbolList()[source_row].destin_name;
|
||||
!symbol_name_v.isValid() ||
|
||||
!symbol_name_v.value<QString>().contains(m_destin_symbol_name, Qt::CaseInsensitive))
|
||||
!symbol_name_v.isValid() || !static_cast<const QString*>(symbol_name_v.data())
|
||||
->contains(m_destin_symbol_name, Qt::CaseInsensitive))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BranchWatchProxyModel::OnDelete(QModelIndexList index_list)
|
||||
{
|
||||
std::transform(index_list.begin(), index_list.end(), index_list.begin(),
|
||||
[this](const QModelIndex& index) { return mapToSource(index); });
|
||||
sourceModel()->OnDelete(std::move(index_list));
|
||||
}
|
||||
|
||||
static constexpr bool BranchSavesLR(UGeckoInstruction inst)
|
||||
{
|
||||
DEBUG_ASSERT(inst.OPCD == 18 || inst.OPCD == 16 ||
|
||||
|
@ -201,7 +201,6 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
|
|||
{
|
||||
setWindowTitle(tr("Branch Watch Tool"));
|
||||
setWindowFlags((windowFlags() | Qt::WindowMinMaxButtonsHint) & ~Qt::WindowContextHelpButtonHint);
|
||||
SetQWidgetWindowDecorations(this);
|
||||
setLayout([this, &ppc_symbol_db]() {
|
||||
auto* main_layout = new QVBoxLayout;
|
||||
|
||||
|
@ -216,6 +215,7 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
|
|||
m_table_proxy->setSourceModel(
|
||||
m_table_model = new BranchWatchTableModel(m_system, m_branch_watch, ppc_symbol_db));
|
||||
m_table_proxy->setSortRole(UserRole::SortRole);
|
||||
m_table_proxy->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
||||
m_table_model->setFont(ui_settings.GetDebugFont());
|
||||
connect(&ui_settings, &Settings::DebugFontChanged, m_table_model,
|
||||
|
@ -234,6 +234,11 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
|
|||
table_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
table_view->setCornerButtonEnabled(false);
|
||||
table_view->verticalHeader()->hide();
|
||||
table_view->setColumnWidth(Column::Instruction, 50);
|
||||
table_view->setColumnWidth(Column::Condition, 50);
|
||||
table_view->setColumnWidth(Column::OriginSymbol, 250);
|
||||
table_view->setColumnWidth(Column::DestinSymbol, 250);
|
||||
// The default column width (100 units) is fine for the rest.
|
||||
|
||||
QHeaderView* const horizontal_header = table_view->horizontalHeader();
|
||||
horizontal_header->restoreState( // Restore column visibility state.
|
||||
|
@ -486,25 +491,19 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
|
|||
return group_box;
|
||||
}());
|
||||
|
||||
UpdateIcons();
|
||||
|
||||
connect(m_timer = new QTimer, &QTimer::timeout, this, &BranchWatchDialog::OnTimeout);
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
||||
&BranchWatchDialog::OnEmulationStateChanged);
|
||||
connect(&Settings::Instance(), &Settings::ThemeChanged, this,
|
||||
&BranchWatchDialog::OnThemeChanged);
|
||||
connect(m_table_proxy, &BranchWatchProxyModel::layoutChanged, this,
|
||||
&BranchWatchDialog::UpdateStatus);
|
||||
|
||||
return main_layout;
|
||||
}());
|
||||
|
||||
// FIXME: On Linux, Qt6 has recently been resetting column widths to their defaults in many
|
||||
// unexpected ways. This affects all kinds of QTables in Dolphin's GUI, so to avoid it in
|
||||
// this QTableView, I have deferred this operation. Any earlier, and this would be undone.
|
||||
// SetQWidgetWindowDecorations was moved to before these operations for the same reason.
|
||||
m_table_view->setColumnWidth(Column::Instruction, 50);
|
||||
m_table_view->setColumnWidth(Column::Condition, 50);
|
||||
m_table_view->setColumnWidth(Column::OriginSymbol, 250);
|
||||
m_table_view->setColumnWidth(Column::DestinSymbol, 250);
|
||||
// The default column width (100 units) is fine for the rest.
|
||||
|
||||
const auto& settings = Settings::GetQSettings();
|
||||
restoreGeometry(settings.value(QStringLiteral("branchwatchdialog/geometry")).toByteArray());
|
||||
}
|
||||
|
@ -596,7 +595,7 @@ void BranchWatchDialog::OnSaveAs()
|
|||
}
|
||||
|
||||
const QString filepath = DolphinFileDialog::getSaveFileName(
|
||||
this, tr("Save Branch Watch snapshot"),
|
||||
this, tr("Save Branch Watch Snapshot"),
|
||||
QString::fromStdString(File::GetUserPath(D_DUMPDEBUG_BRANCHWATCH_IDX)),
|
||||
tr("Text file (*.txt);;All Files (*)"));
|
||||
if (filepath.isEmpty())
|
||||
|
@ -613,7 +612,7 @@ void BranchWatchDialog::OnLoad()
|
|||
void BranchWatchDialog::OnLoadFrom()
|
||||
{
|
||||
const QString filepath = DolphinFileDialog::getOpenFileName(
|
||||
this, tr("Load Branch Watch snapshot"),
|
||||
this, tr("Load Branch Watch Snapshot"),
|
||||
QString::fromStdString(File::GetUserPath(D_DUMPDEBUG_BRANCHWATCH_IDX)),
|
||||
tr("Text file (*.txt);;All Files (*)"), nullptr, QFileDialog::Option::ReadOnly);
|
||||
if (filepath.isEmpty())
|
||||
|
@ -700,13 +699,19 @@ void BranchWatchDialog::OnEmulationStateChanged(Core::State new_state)
|
|||
Update();
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnThemeChanged()
|
||||
{
|
||||
UpdateIcons();
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnHelp()
|
||||
{
|
||||
ModalMessageBox::information(
|
||||
this, tr("Branch Watch Tool Help (1/4)"),
|
||||
tr("Branch Watch is a code-searching tool that can isolate branches tracked by the emulated "
|
||||
"CPU by testing candidate branches with simple criteria. If you are familiar with Cheat "
|
||||
"Engine's Ultimap, Branch Watch is similar to that.\n\n"
|
||||
"Engine's Ultimap, Branch Watch is similar to that."
|
||||
"\n\n"
|
||||
"Press the \"Start Branch Watch\" button to activate Branch Watch. Branch Watch persists "
|
||||
"across emulation sessions, and a snapshot of your progress can be saved to and loaded "
|
||||
"from the User Directory to persist after Dolphin Emulator is closed. \"Save As...\" and "
|
||||
|
@ -729,7 +734,8 @@ void BranchWatchDialog::OnHelp()
|
|||
"taken since the last time it was checked. It is also possible to reduce the candidates "
|
||||
"by determining whether a branch instruction has or has not been overwritten since it was "
|
||||
"first hit. Filter the candidates by branch kind, branch condition, origin or destination "
|
||||
"address, and origin or destination symbol name.\n\n"
|
||||
"address, and origin or destination symbol name."
|
||||
"\n\n"
|
||||
"After enough passes and experimentation, you may be able to find function calls and "
|
||||
"conditional code paths that are only taken when an action is performed in the emulated "
|
||||
"software."));
|
||||
|
@ -737,17 +743,27 @@ void BranchWatchDialog::OnHelp()
|
|||
this, tr("Branch Watch Tool Help (4/4)"),
|
||||
tr("Rows in the table can be left-clicked on the origin, destination, and symbol columns to "
|
||||
"view the associated address in Code View. Right-clicking the selected row(s) will bring "
|
||||
"up a context menu.\n\n"
|
||||
"up a context menu."
|
||||
"\n\n"
|
||||
"If the origin, destination, or symbol columns are right-clicked, an action copy the "
|
||||
"relevant address(es) to the clipboard will be available, and an action to set a "
|
||||
"breakpoint at the relevant address(es) will be available. Note that, for the origin / "
|
||||
"destination symbol columns, these actions will only be enabled if every row in the "
|
||||
"selection has a symbol."
|
||||
"\n\n"
|
||||
"If the origin column of a row selection is right-clicked, an action to replace the "
|
||||
"branch instruction at the origin(s) with a NOP instruction (No Operation), and an action "
|
||||
"to copy the address(es) to the clipboard will be available.\n\n"
|
||||
"branch instruction at the origin(s) with a NOP instruction (No Operation) will be "
|
||||
"available."
|
||||
"\n\n"
|
||||
"If the destination column of a row selection is right-clicked, an action to replace the "
|
||||
"instruction at the destination(s) with a BLR instruction (Branch to Link Register) will "
|
||||
"be available, but only if the branch instruction at every origin saves the link "
|
||||
"register, and an action to copy the address(es) to the clipboard will be available.\n\n"
|
||||
"be available, but will only be enabled if the branch instruction at every origin updates "
|
||||
"the link register."
|
||||
"\n\n"
|
||||
"If the origin / destination symbol column of a row selection is right-clicked, an action "
|
||||
"to replace the instruction(s) at the start of the symbol with a BLR instruction will be "
|
||||
"available, but only if every origin / destination symbol is found.\n\n"
|
||||
"to replace the instruction at the start of the symbol(s) with a BLR instruction will be "
|
||||
"available, but will only be enabled if every row in the selection has a symbol."
|
||||
"\n\n"
|
||||
"All context menus have the action to delete the selected row(s) from the candidates."));
|
||||
}
|
||||
|
||||
|
@ -759,7 +775,7 @@ void BranchWatchDialog::OnToggleAutoSave(bool checked)
|
|||
const QString filepath = DolphinFileDialog::getSaveFileName(
|
||||
// i18n: If the user selects a file, Branch Watch will save to that file.
|
||||
// If the user presses Cancel, Branch Watch will save to a file in the user folder.
|
||||
this, tr("Select Branch Watch snapshot auto-save file (for user folder location, cancel)"),
|
||||
this, tr("Select Branch Watch Snapshot Auto-Save File (for user folder location, cancel)"),
|
||||
QString::fromStdString(File::GetUserPath(D_DUMPDEBUG_BRANCHWATCH_IDX)),
|
||||
tr("Text file (*.txt);;All Files (*)"));
|
||||
if (filepath.isEmpty())
|
||||
|
@ -800,68 +816,18 @@ void BranchWatchDialog::OnTableClicked(const QModelIndex& index)
|
|||
|
||||
void BranchWatchDialog::OnTableContextMenu(const QPoint& pos)
|
||||
{
|
||||
if (m_table_view->horizontalHeader()->hiddenSectionCount() == Column::NumberOfColumns)
|
||||
{
|
||||
m_mnu_column_visibility->exec(m_table_view->viewport()->mapToGlobal(pos));
|
||||
return;
|
||||
}
|
||||
const QModelIndex index = m_table_view->indexAt(pos);
|
||||
if (!index.isValid())
|
||||
return;
|
||||
QModelIndexList index_list = m_table_view->selectionModel()->selectedRows(index.column());
|
||||
|
||||
QMenu* const menu = new QMenu;
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
|
||||
menu->addAction(tr("&Delete"), [this, index_list]() { OnTableDelete(std::move(index_list)); });
|
||||
switch (index.column())
|
||||
{
|
||||
case Column::Origin:
|
||||
{
|
||||
QAction* const action = menu->addAction(tr("Insert &NOP"));
|
||||
if (Core::GetState(m_system) != Core::State::Uninitialized)
|
||||
connect(action, &QAction::triggered,
|
||||
[this, index_list]() { OnTableSetNOP(std::move(index_list)); });
|
||||
else
|
||||
action->setEnabled(false);
|
||||
menu->addAction(tr("&Copy Address"), [this, index_list = std::move(index_list)]() {
|
||||
OnTableCopyAddress(std::move(index_list));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Column::Destination:
|
||||
{
|
||||
QAction* const action = menu->addAction(tr("Insert &BLR"));
|
||||
const bool enable_action =
|
||||
Core::GetState(m_system) != Core::State::Uninitialized &&
|
||||
std::all_of(index_list.begin(), index_list.end(), [this](const QModelIndex& idx) {
|
||||
const QModelIndex sibling = idx.siblingAtColumn(Column::Instruction);
|
||||
return BranchSavesLR(m_table_proxy->data(sibling, UserRole::ClickRole).value<u32>());
|
||||
});
|
||||
if (enable_action)
|
||||
connect(action, &QAction::triggered,
|
||||
[this, index_list]() { OnTableSetBLR(std::move(index_list)); });
|
||||
else
|
||||
action->setEnabled(false);
|
||||
menu->addAction(tr("&Copy Address"), [this, index_list = std::move(index_list)]() {
|
||||
OnTableCopyAddress(std::move(index_list));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Column::OriginSymbol:
|
||||
case Column::DestinSymbol:
|
||||
{
|
||||
QAction* const action = menu->addAction(tr("Insert &BLR at start"));
|
||||
const bool enable_action =
|
||||
Core::GetState(m_system) != Core::State::Uninitialized &&
|
||||
std::all_of(index_list.begin(), index_list.end(), [this](const QModelIndex& idx) {
|
||||
return m_table_proxy->data(idx, UserRole::ClickRole).isValid();
|
||||
});
|
||||
if (enable_action)
|
||||
connect(action, &QAction::triggered, [this, index_list = std::move(index_list)]() {
|
||||
OnTableSetBLR(std::move(index_list));
|
||||
});
|
||||
else
|
||||
action->setEnabled(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
menu->exec(m_table_view->viewport()->mapToGlobal(pos));
|
||||
m_index_list_temp = m_table_view->selectionModel()->selectedRows(index.column());
|
||||
GetTableContextMenu(index)->exec(m_table_view->viewport()->mapToGlobal(pos));
|
||||
m_index_list_temp.clear();
|
||||
m_index_list_temp.shrink_to_fit();
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableHeaderContextMenu(const QPoint& pos)
|
||||
|
@ -869,67 +835,72 @@ void BranchWatchDialog::OnTableHeaderContextMenu(const QPoint& pos)
|
|||
m_mnu_column_visibility->exec(m_table_view->horizontalHeader()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableDelete(QModelIndexList index_list)
|
||||
void BranchWatchDialog::OnTableDelete()
|
||||
{
|
||||
m_table_proxy->OnDelete(std::move(index_list));
|
||||
std::ranges::transform(
|
||||
m_index_list_temp, m_index_list_temp.begin(),
|
||||
[this](const QModelIndex& index) { return m_table_proxy->mapToSource(index); });
|
||||
std::ranges::sort(m_index_list_temp, std::less{});
|
||||
for (const auto& index : std::ranges::reverse_view{m_index_list_temp})
|
||||
{
|
||||
if (!index.isValid())
|
||||
continue;
|
||||
m_table_model->removeRow(index.row());
|
||||
}
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableDeleteKeypress()
|
||||
{
|
||||
OnTableDelete(m_table_view->selectionModel()->selectedRows());
|
||||
m_index_list_temp = m_table_view->selectionModel()->selectedRows();
|
||||
OnTableDelete();
|
||||
m_index_list_temp.clear();
|
||||
m_index_list_temp.shrink_to_fit();
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableSetBLR(QModelIndexList index_list)
|
||||
void BranchWatchDialog::OnTableSetBLR()
|
||||
{
|
||||
for (const QModelIndex& index : index_list)
|
||||
{
|
||||
m_system.GetPowerPC().GetDebugInterface().SetPatch(
|
||||
Core::CPUThreadGuard{m_system},
|
||||
m_table_proxy->data(index, UserRole::ClickRole).value<u32>(), 0x4e800020);
|
||||
m_table_proxy->SetInspected(index);
|
||||
}
|
||||
// TODO: This is not ideal. What I need is a signal for when memory has been changed by the GUI,
|
||||
// but I cannot find one. UpdateDisasmDialog comes close, but does too much in one signal. For
|
||||
// example, CodeViewWidget will scroll to the current PC when UpdateDisasmDialog is signaled. This
|
||||
// seems like a pervasive issue. For example, modifying an instruction in the CodeViewWidget will
|
||||
// not reflect in the MemoryViewWidget, and vice versa. Neither of these widgets changing memory
|
||||
// will reflect in the JITWidget, either. At the very least, we can make sure the CodeWidget
|
||||
// is updated in an acceptable way.
|
||||
m_code_widget->Update();
|
||||
SetStubPatches(0x4e800020);
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableSetNOP(QModelIndexList index_list)
|
||||
void BranchWatchDialog::OnTableSetNOP()
|
||||
{
|
||||
for (const QModelIndex& index : index_list)
|
||||
{
|
||||
m_system.GetPowerPC().GetDebugInterface().SetPatch(
|
||||
Core::CPUThreadGuard{m_system},
|
||||
m_table_proxy->data(index, UserRole::ClickRole).value<u32>(), 0x60000000);
|
||||
m_table_proxy->SetInspected(index);
|
||||
}
|
||||
// Same issue as OnSetBLR.
|
||||
m_code_widget->Update();
|
||||
SetStubPatches(0x60000000);
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableCopyAddress(QModelIndexList index_list)
|
||||
void BranchWatchDialog::OnTableCopyAddress()
|
||||
{
|
||||
auto iter = index_list.begin();
|
||||
if (iter == index_list.end())
|
||||
auto iter = m_index_list_temp.begin();
|
||||
if (iter == m_index_list_temp.end())
|
||||
return;
|
||||
|
||||
QString text;
|
||||
text.reserve(index_list.size() * 9 - 1);
|
||||
text.reserve(m_index_list_temp.size() * 9 - 1);
|
||||
while (true)
|
||||
{
|
||||
text.append(QString::number(m_table_proxy->data(*iter, UserRole::ClickRole).value<u32>(), 16));
|
||||
if (++iter == index_list.end())
|
||||
if (++iter == m_index_list_temp.end())
|
||||
break;
|
||||
text.append(QChar::fromLatin1('\n'));
|
||||
}
|
||||
QApplication::clipboard()->setText(text);
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableSetBreakpointBreak()
|
||||
{
|
||||
SetBreakpoints(true, false);
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableSetBreakpointLog()
|
||||
{
|
||||
SetBreakpoints(false, true);
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableSetBreakpointBoth()
|
||||
{
|
||||
SetBreakpoints(true, true);
|
||||
}
|
||||
|
||||
void BranchWatchDialog::SaveSettings()
|
||||
{
|
||||
auto& settings = Settings::GetQSettings();
|
||||
|
@ -982,6 +953,12 @@ void BranchWatchDialog::UpdateStatus()
|
|||
}
|
||||
}
|
||||
|
||||
void BranchWatchDialog::UpdateIcons()
|
||||
{
|
||||
m_icn_full = Resources::GetThemeIcon("debugger_breakpoint");
|
||||
m_icn_partial = Resources::GetThemeIcon("stop");
|
||||
}
|
||||
|
||||
void BranchWatchDialog::Save(const Core::CPUThreadGuard& guard, const std::string& filepath)
|
||||
{
|
||||
File::IOFile file(filepath, "w");
|
||||
|
@ -1018,3 +995,144 @@ void BranchWatchDialog::AutoSave(const Core::CPUThreadGuard& guard)
|
|||
return;
|
||||
Save(guard, m_autosave_filepath ? m_autosave_filepath.value() : GetSnapshotDefaultFilepath());
|
||||
}
|
||||
|
||||
void BranchWatchDialog::SetStubPatches(u32 value) const
|
||||
{
|
||||
auto& debug_interface = m_system.GetPowerPC().GetDebugInterface();
|
||||
for (const Core::CPUThreadGuard guard(m_system); const QModelIndex& index : m_index_list_temp)
|
||||
{
|
||||
debug_interface.SetPatch(guard, m_table_proxy->data(index, UserRole::ClickRole).value<u32>(),
|
||||
value);
|
||||
m_table_proxy->SetInspected(index);
|
||||
}
|
||||
// TODO: This is not ideal. What I need is a signal for when memory has been changed by the GUI,
|
||||
// but I cannot find one. UpdateDisasmDialog comes close, but does too much in one signal. For
|
||||
// example, CodeViewWidget will scroll to the current PC when UpdateDisasmDialog is signaled. This
|
||||
// seems like a pervasive issue. For example, modifying an instruction in the CodeViewWidget will
|
||||
// not reflect in the MemoryViewWidget, and vice versa. Neither of these widgets changing memory
|
||||
// will reflect in the JITWidget, either. At the very least, we can make sure the CodeWidget
|
||||
// is updated in an acceptable way.
|
||||
m_code_widget->Update();
|
||||
}
|
||||
|
||||
void BranchWatchDialog::SetBreakpoints(bool break_on_hit, bool log_on_hit) const
|
||||
{
|
||||
auto& breakpoints = m_system.GetPowerPC().GetBreakPoints();
|
||||
for (const QModelIndex& index : m_index_list_temp)
|
||||
{
|
||||
const u32 address = m_table_proxy->data(index, UserRole::ClickRole).value<u32>();
|
||||
breakpoints.Add(address, break_on_hit, log_on_hit, {});
|
||||
}
|
||||
emit m_code_widget->BreakpointsChanged();
|
||||
m_code_widget->Update();
|
||||
}
|
||||
|
||||
QMenu* BranchWatchDialog::GetTableContextMenu(const QModelIndex& index)
|
||||
{
|
||||
if (m_mnu_table_context == nullptr)
|
||||
{
|
||||
m_mnu_table_context = new QMenu(this);
|
||||
m_mnu_table_context->addAction(tr("&Delete"), this, &BranchWatchDialog::OnTableDelete);
|
||||
m_act_insert_nop =
|
||||
m_mnu_table_context->addAction(tr("Insert &NOP"), this, &BranchWatchDialog::OnTableSetNOP);
|
||||
m_act_insert_blr =
|
||||
m_mnu_table_context->addAction(tr("Insert &BLR"), this, &BranchWatchDialog::OnTableSetBLR);
|
||||
m_act_copy_address = m_mnu_table_context->addAction(tr("&Copy Address"), this,
|
||||
&BranchWatchDialog::OnTableCopyAddress);
|
||||
|
||||
m_mnu_set_breakpoint = new QMenu(tr("Set Brea&kpoint"));
|
||||
m_act_break_on_hit = m_mnu_set_breakpoint->addAction(
|
||||
tr("&Break on Hit"), this, &BranchWatchDialog::OnTableSetBreakpointBreak);
|
||||
m_act_log_on_hit = m_mnu_set_breakpoint->addAction(tr("&Log on Hit"), this,
|
||||
&BranchWatchDialog::OnTableSetBreakpointLog);
|
||||
m_act_both_on_hit = m_mnu_set_breakpoint->addAction(
|
||||
tr("Break &and Log on Hit"), this, &BranchWatchDialog::OnTableSetBreakpointBoth);
|
||||
m_mnu_table_context->addMenu(m_mnu_set_breakpoint);
|
||||
}
|
||||
|
||||
const bool core_initialized = Core::GetState(m_system) != Core::State::Uninitialized;
|
||||
|
||||
bool supported_column = true;
|
||||
switch (index.column())
|
||||
{
|
||||
case Column::Origin:
|
||||
m_act_insert_blr->setVisible(false);
|
||||
m_act_insert_nop->setVisible(true);
|
||||
m_act_insert_nop->setEnabled(core_initialized);
|
||||
m_act_copy_address->setEnabled(true);
|
||||
m_mnu_set_breakpoint->setEnabled(true);
|
||||
break;
|
||||
case Column::Destination:
|
||||
{
|
||||
m_act_insert_nop->setVisible(false);
|
||||
m_act_insert_blr->setVisible(true);
|
||||
const bool all_branches_save_lr =
|
||||
core_initialized &&
|
||||
std::all_of(
|
||||
m_index_list_temp.begin(), m_index_list_temp.end(), [this](const QModelIndex& idx) {
|
||||
const QModelIndex sibling = idx.siblingAtColumn(Column::Instruction);
|
||||
return BranchSavesLR(m_table_proxy->data(sibling, UserRole::ClickRole).value<u32>());
|
||||
});
|
||||
m_act_insert_blr->setEnabled(all_branches_save_lr);
|
||||
m_act_copy_address->setEnabled(true);
|
||||
m_mnu_set_breakpoint->setEnabled(true);
|
||||
break;
|
||||
}
|
||||
case Column::OriginSymbol:
|
||||
case Column::DestinSymbol:
|
||||
{
|
||||
m_act_insert_nop->setVisible(false);
|
||||
m_act_insert_blr->setVisible(true);
|
||||
const bool all_symbols_valid =
|
||||
core_initialized &&
|
||||
std::all_of(m_index_list_temp.begin(), m_index_list_temp.end(),
|
||||
[this](const QModelIndex& idx) {
|
||||
return m_table_proxy->data(idx, UserRole::ClickRole).isValid();
|
||||
});
|
||||
m_act_insert_blr->setEnabled(all_symbols_valid);
|
||||
m_act_copy_address->setEnabled(all_symbols_valid);
|
||||
m_mnu_set_breakpoint->setEnabled(all_symbols_valid);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
m_act_insert_nop->setVisible(false);
|
||||
m_act_insert_blr->setVisible(false);
|
||||
supported_column = false;
|
||||
break;
|
||||
}
|
||||
m_act_copy_address->setVisible(supported_column);
|
||||
m_mnu_set_breakpoint->menuAction()->setVisible(supported_column);
|
||||
// Setting breakpoints while nothing is being emulated is discouraged in the UI.
|
||||
m_mnu_set_breakpoint->setEnabled(core_initialized);
|
||||
|
||||
if (core_initialized && supported_column)
|
||||
{
|
||||
qsizetype bp_break_count = 0, bp_log_count = 0, bp_both_count = 0;
|
||||
for (auto& breakpoints = m_system.GetPowerPC().GetBreakPoints();
|
||||
const QModelIndex& idx : m_index_list_temp)
|
||||
{
|
||||
if (const TBreakPoint* bp = breakpoints.GetRegularBreakpoint(
|
||||
m_table_proxy->data(idx, UserRole::ClickRole).value<u32>()))
|
||||
{
|
||||
if (bp->break_on_hit && bp->log_on_hit)
|
||||
{
|
||||
bp_both_count += 1;
|
||||
continue;
|
||||
}
|
||||
bp_break_count += bp->break_on_hit;
|
||||
bp_log_count += bp->log_on_hit;
|
||||
}
|
||||
}
|
||||
m_act_break_on_hit->setIconVisibleInMenu(bp_break_count != 0);
|
||||
m_act_break_on_hit->setIcon(bp_break_count == m_index_list_temp.size() ? m_icn_full :
|
||||
m_icn_partial);
|
||||
m_act_log_on_hit->setIconVisibleInMenu(bp_log_count != 0);
|
||||
m_act_log_on_hit->setIcon(bp_log_count == m_index_list_temp.size() ? m_icn_full :
|
||||
m_icn_partial);
|
||||
m_act_both_on_hit->setIconVisibleInMenu(bp_both_count != 0);
|
||||
m_act_both_on_hit->setIcon(bp_both_count == m_index_list_temp.size() ? m_icn_full :
|
||||
m_icn_partial);
|
||||
}
|
||||
|
||||
return m_mnu_table_context;
|
||||
}
|
||||
|
|
|
@ -7,14 +7,16 @@
|
|||
#include <string>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QIcon>
|
||||
#include <QModelIndexList>
|
||||
|
||||
#include "Core/Core.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class BranchWatch;
|
||||
class CPUThreadGuard;
|
||||
enum class State;
|
||||
class System;
|
||||
} // namespace Core
|
||||
class PPCSymbolDB;
|
||||
|
@ -54,6 +56,11 @@ public:
|
|||
QWidget* parent = nullptr);
|
||||
~BranchWatchDialog() override;
|
||||
|
||||
BranchWatchDialog(const BranchWatchDialog&) = delete;
|
||||
BranchWatchDialog(BranchWatchDialog&&) = delete;
|
||||
BranchWatchDialog& operator=(const BranchWatchDialog&) = delete;
|
||||
BranchWatchDialog& operator=(BranchWatchDialog&&) = delete;
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
@ -73,6 +80,7 @@ private:
|
|||
void OnWipeInspection();
|
||||
void OnTimeout();
|
||||
void OnEmulationStateChanged(Core::State new_state);
|
||||
void OnThemeChanged();
|
||||
void OnHelp();
|
||||
void OnToggleAutoSave(bool checked);
|
||||
void OnHideShowControls(bool checked);
|
||||
|
@ -81,11 +89,14 @@ private:
|
|||
void OnTableClicked(const QModelIndex& index);
|
||||
void OnTableContextMenu(const QPoint& pos);
|
||||
void OnTableHeaderContextMenu(const QPoint& pos);
|
||||
void OnTableDelete(QModelIndexList index_list);
|
||||
void OnTableDelete();
|
||||
void OnTableDeleteKeypress();
|
||||
void OnTableSetBLR(QModelIndexList index_list);
|
||||
void OnTableSetNOP(QModelIndexList index_list);
|
||||
void OnTableCopyAddress(QModelIndexList index_list);
|
||||
void OnTableSetBLR();
|
||||
void OnTableSetNOP();
|
||||
void OnTableCopyAddress();
|
||||
void OnTableSetBreakpointBreak();
|
||||
void OnTableSetBreakpointLog();
|
||||
void OnTableSetBreakpointBoth();
|
||||
|
||||
void SaveSettings();
|
||||
|
||||
|
@ -95,9 +106,14 @@ public:
|
|||
|
||||
private:
|
||||
void UpdateStatus();
|
||||
void UpdateIcons();
|
||||
void Save(const Core::CPUThreadGuard& guard, const std::string& filepath);
|
||||
void Load(const Core::CPUThreadGuard& guard, const std::string& filepath);
|
||||
void AutoSave(const Core::CPUThreadGuard& guard);
|
||||
void SetStubPatches(u32 value) const;
|
||||
void SetBreakpoints(bool break_on_hit, bool log_on_hit) const;
|
||||
|
||||
[[nodiscard]] QMenu* GetTableContextMenu(const QModelIndex& index);
|
||||
|
||||
Core::System& m_system;
|
||||
Core::BranchWatch& m_branch_watch;
|
||||
|
@ -106,6 +122,14 @@ private:
|
|||
QPushButton *m_btn_start_pause, *m_btn_clear_watch, *m_btn_path_was_taken, *m_btn_path_not_taken,
|
||||
*m_btn_was_overwritten, *m_btn_not_overwritten, *m_btn_wipe_recent_hits;
|
||||
QAction* m_act_autosave;
|
||||
QAction* m_act_insert_nop;
|
||||
QAction* m_act_insert_blr;
|
||||
QAction* m_act_copy_address;
|
||||
QMenu* m_mnu_set_breakpoint;
|
||||
QAction* m_act_break_on_hit;
|
||||
QAction* m_act_log_on_hit;
|
||||
QAction* m_act_both_on_hit;
|
||||
QMenu* m_mnu_table_context = nullptr;
|
||||
QMenu* m_mnu_column_visibility;
|
||||
|
||||
QToolBar* m_control_toolbar;
|
||||
|
@ -115,5 +139,8 @@ private:
|
|||
QStatusBar* m_status_bar;
|
||||
QTimer* m_timer;
|
||||
|
||||
QIcon m_icn_full, m_icn_partial;
|
||||
|
||||
QModelIndexList m_index_list_temp;
|
||||
std::optional<std::string> m_autosave_filepath;
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/GekkoDisassembler.h"
|
||||
#include "Common/Unreachable.h"
|
||||
#include "Core/Debugger/BranchWatch.h"
|
||||
#include "Core/PowerPC/PPCSymbolDB.h"
|
||||
|
||||
|
@ -144,18 +145,6 @@ void BranchWatchTableModel::OnWipeInspection()
|
|||
roles);
|
||||
}
|
||||
|
||||
void BranchWatchTableModel::OnDelete(QModelIndexList index_list)
|
||||
{
|
||||
std::sort(index_list.begin(), index_list.end());
|
||||
// TODO C++20: std::ranges::reverse_view
|
||||
for (auto iter = index_list.rbegin(); iter != index_list.rend(); ++iter)
|
||||
{
|
||||
if (!iter->isValid())
|
||||
continue;
|
||||
removeRow(iter->row());
|
||||
}
|
||||
}
|
||||
|
||||
void BranchWatchTableModel::Save(const Core::CPUThreadGuard& guard, std::FILE* file) const
|
||||
{
|
||||
m_branch_watch.Save(guard, file);
|
||||
|
@ -355,7 +344,8 @@ QVariant BranchWatchTableModel::DisplayRoleData(const QModelIndex& index) const
|
|||
case Column::TotalHits:
|
||||
return QString::number(kv->second.total_hits);
|
||||
}
|
||||
return QVariant();
|
||||
static_assert(Column::NumberOfColumns == 8);
|
||||
Common::Unreachable();
|
||||
}
|
||||
|
||||
QVariant BranchWatchTableModel::FontRoleData(const QModelIndex& index) const
|
||||
|
@ -400,7 +390,8 @@ QVariant BranchWatchTableModel::TextAlignmentRoleData(const QModelIndex& index)
|
|||
case Column::DestinSymbol:
|
||||
return QVariant::fromValue(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
}
|
||||
return QVariant();
|
||||
static_assert(Column::NumberOfColumns == 8);
|
||||
Common::Unreachable();
|
||||
}
|
||||
|
||||
QVariant BranchWatchTableModel::ForegroundRoleData(const QModelIndex& index) const
|
||||
|
@ -498,5 +489,6 @@ QVariant BranchWatchTableModel::SortRoleData(const QModelIndex& index) const
|
|||
case Column::TotalHits:
|
||||
return qulonglong{kv->second.total_hits};
|
||||
}
|
||||
return QVariant();
|
||||
static_assert(Column::NumberOfColumns == 8);
|
||||
Common::Unreachable();
|
||||
}
|
||||
|
|
|
@ -75,6 +75,13 @@ public:
|
|||
m_ppc_symbol_db(ppc_symbol_db)
|
||||
{
|
||||
}
|
||||
~BranchWatchTableModel() override = default;
|
||||
|
||||
BranchWatchTableModel(const BranchWatchTableModel&) = delete;
|
||||
BranchWatchTableModel(BranchWatchTableModel&&) = delete;
|
||||
BranchWatchTableModel& operator=(const BranchWatchTableModel&) = delete;
|
||||
BranchWatchTableModel& operator=(BranchWatchTableModel&&) = delete;
|
||||
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const override;
|
||||
|
@ -90,7 +97,6 @@ public:
|
|||
void OnBranchNotOverwritten(const Core::CPUThreadGuard& guard);
|
||||
void OnWipeRecentHits();
|
||||
void OnWipeInspection();
|
||||
void OnDelete(QModelIndexList index_list);
|
||||
|
||||
void Save(const Core::CPUThreadGuard& guard, std::FILE* file) const;
|
||||
void Load(const Core::CPUThreadGuard& guard, std::FILE* file);
|
||||
|
|
|
@ -289,7 +289,7 @@ void BreakpointDialog::accept()
|
|||
return;
|
||||
}
|
||||
|
||||
m_parent->AddBP(address, false, do_break, do_log, condition);
|
||||
m_parent->AddBP(address, do_break, do_log, condition);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -3,9 +3,15 @@
|
|||
|
||||
#include "DolphinQt/Debugger/BreakpointWidget.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QHeaderView>
|
||||
#include <QInputDialog>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QSignalBlocker>
|
||||
#include <QStyleOptionViewItem>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTableWidget>
|
||||
#include <QToolBar>
|
||||
#include <QVBoxLayout>
|
||||
|
@ -34,7 +40,55 @@ enum CustomRole
|
|||
ADDRESS_ROLE = Qt::UserRole,
|
||||
IS_MEMCHECK_ROLE
|
||||
};
|
||||
}
|
||||
|
||||
enum TableColumns
|
||||
{
|
||||
ENABLED_COLUMN = 0,
|
||||
TYPE_COLUMN = 1,
|
||||
SYMBOL_COLUMN = 2,
|
||||
ADDRESS_COLUMN = 3,
|
||||
END_ADDRESS_COLUMN = 4,
|
||||
BREAK_COLUMN = 5,
|
||||
LOG_COLUMN = 6,
|
||||
READ_COLUMN = 7,
|
||||
WRITE_COLUMN = 8,
|
||||
CONDITION_COLUMN = 9,
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Fix icons not centering properly in a QTableWidget.
|
||||
class CustomDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
CustomDelegate(BreakpointWidget* parent) : QStyledItemDelegate(parent) {}
|
||||
|
||||
private:
|
||||
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||
{
|
||||
Q_ASSERT(index.isValid());
|
||||
|
||||
// Fetch normal drawing logic.
|
||||
QStyleOptionViewItem opt = option;
|
||||
initStyleOption(&opt, index);
|
||||
|
||||
// Disable drawing icon the normal way.
|
||||
opt.icon = QIcon();
|
||||
opt.decorationSize = QSize(0, 0);
|
||||
|
||||
// Default draw command for paint.
|
||||
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, 0);
|
||||
|
||||
// Draw pixmap at the center of the tablewidget cell
|
||||
QPixmap pix = qvariant_cast<QPixmap>(index.data(Qt::DecorationRole));
|
||||
if (!pix.isNull())
|
||||
{
|
||||
const QRect r = option.rect;
|
||||
const QSize size = pix.deviceIndependentSize().toSize();
|
||||
const QPoint p = QPoint((r.width() - size.width()) / 2, (r.height() - size.height()) / 2);
|
||||
painter->drawPixmap(r.topLeft() + p, pix);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BreakpointWidget::BreakpointWidget(QWidget* parent)
|
||||
: QDockWidget(parent), m_system(Core::System::GetInstance())
|
||||
|
@ -62,6 +116,8 @@ BreakpointWidget::BreakpointWidget(QWidget* parent)
|
|||
Update();
|
||||
});
|
||||
|
||||
connect(m_table, &QTableWidget::itemChanged, this, &BreakpointWidget::OnItemChanged);
|
||||
|
||||
connect(&Settings::Instance(), &Settings::BreakpointsVisibilityChanged, this,
|
||||
[this](bool visible) { setHidden(!visible); });
|
||||
|
||||
|
@ -69,7 +125,11 @@ BreakpointWidget::BreakpointWidget(QWidget* parent)
|
|||
setHidden(!enabled || !Settings::Instance().IsBreakpointsVisible());
|
||||
});
|
||||
|
||||
connect(&Settings::Instance(), &Settings::ThemeChanged, this, &BreakpointWidget::UpdateIcons);
|
||||
connect(&Settings::Instance(), &Settings::ThemeChanged, this, [this]() {
|
||||
UpdateIcons();
|
||||
Update();
|
||||
});
|
||||
|
||||
UpdateIcons();
|
||||
}
|
||||
|
||||
|
@ -88,14 +148,14 @@ void BreakpointWidget::CreateWidgets()
|
|||
m_toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
|
||||
m_table = new QTableWidget;
|
||||
m_table->setItemDelegate(new CustomDelegate(this));
|
||||
m_table->setTabKeyNavigation(false);
|
||||
m_table->setContentsMargins(0, 0, 0, 0);
|
||||
m_table->setColumnCount(6);
|
||||
m_table->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
m_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
m_table->setColumnCount(10);
|
||||
m_table->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
m_table->verticalHeader()->hide();
|
||||
|
||||
connect(m_table, &QTableWidget::itemClicked, this, &BreakpointWidget::OnClicked);
|
||||
connect(m_table, &QTableWidget::customContextMenuRequested, this,
|
||||
&BreakpointWidget::OnContextMenu);
|
||||
|
||||
|
@ -109,13 +169,11 @@ void BreakpointWidget::CreateWidgets()
|
|||
layout->setSpacing(0);
|
||||
|
||||
m_new = m_toolbar->addAction(tr("New"), this, &BreakpointWidget::OnNewBreakpoint);
|
||||
m_delete = m_toolbar->addAction(tr("Delete"), this, &BreakpointWidget::OnDelete);
|
||||
m_clear = m_toolbar->addAction(tr("Clear"), this, &BreakpointWidget::OnClear);
|
||||
|
||||
m_load = m_toolbar->addAction(tr("Load"), this, &BreakpointWidget::OnLoad);
|
||||
m_save = m_toolbar->addAction(tr("Save"), this, &BreakpointWidget::OnSave);
|
||||
|
||||
m_new->setEnabled(false);
|
||||
m_load->setEnabled(false);
|
||||
m_save->setEnabled(false);
|
||||
|
||||
|
@ -128,7 +186,6 @@ void BreakpointWidget::CreateWidgets()
|
|||
void BreakpointWidget::UpdateIcons()
|
||||
{
|
||||
m_new->setIcon(Resources::GetThemeIcon("debugger_add_breakpoint"));
|
||||
m_delete->setIcon(Resources::GetThemeIcon("debugger_delete"));
|
||||
m_clear->setIcon(Resources::GetThemeIcon("debugger_clear"));
|
||||
m_load->setIcon(Resources::GetThemeIcon("debugger_load"));
|
||||
m_save->setIcon(Resources::GetThemeIcon("debugger_save"));
|
||||
|
@ -145,13 +202,59 @@ void BreakpointWidget::showEvent(QShowEvent* event)
|
|||
Update();
|
||||
}
|
||||
|
||||
void BreakpointWidget::OnClicked(QTableWidgetItem* item)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
if (item->column() == SYMBOL_COLUMN || item->column() == ADDRESS_COLUMN ||
|
||||
item->column() == END_ADDRESS_COLUMN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 address = static_cast<u32>(m_table->item(item->row(), 0)->data(ADDRESS_ROLE).toUInt());
|
||||
|
||||
if (item->column() == ENABLED_COLUMN)
|
||||
{
|
||||
if (item->data(IS_MEMCHECK_ROLE).toBool())
|
||||
m_system.GetPowerPC().GetMemChecks().ToggleEnable(address);
|
||||
else
|
||||
m_system.GetPowerPC().GetBreakPoints().ToggleEnable(address);
|
||||
|
||||
emit BreakpointsChanged();
|
||||
Update();
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<QString> string = std::nullopt;
|
||||
|
||||
if (item->column() == CONDITION_COLUMN)
|
||||
{
|
||||
bool ok;
|
||||
QString new_text = QInputDialog::getMultiLineText(
|
||||
this, tr("Edit Conditional"), tr("Edit conditional expression"), item->text(), &ok);
|
||||
|
||||
if (!ok || item->text() == new_text)
|
||||
return;
|
||||
|
||||
// If new_text is empty, leaving string as nullopt will clear the conditional.
|
||||
if (!new_text.isEmpty())
|
||||
string = new_text;
|
||||
}
|
||||
|
||||
if (m_table->item(item->row(), 0)->data(IS_MEMCHECK_ROLE).toBool())
|
||||
EditMBP(address, item->column(), string);
|
||||
else
|
||||
EditBreakpoint(address, item->column(), string);
|
||||
}
|
||||
|
||||
void BreakpointWidget::UpdateButtonsEnabled()
|
||||
{
|
||||
if (!isVisible())
|
||||
return;
|
||||
|
||||
const bool is_initialised = Core::GetState(m_system) != Core::State::Uninitialized;
|
||||
m_new->setEnabled(is_initialised);
|
||||
m_load->setEnabled(is_initialised);
|
||||
m_save->setEnabled(is_initialised);
|
||||
}
|
||||
|
@ -161,20 +264,43 @@ void BreakpointWidget::Update()
|
|||
if (!isVisible())
|
||||
return;
|
||||
|
||||
m_table->clear();
|
||||
const QSignalBlocker blocker(m_table);
|
||||
|
||||
m_table->setHorizontalHeaderLabels(
|
||||
{tr("Active"), tr("Type"), tr("Function"), tr("Address"), tr("Flags"), tr("Condition")});
|
||||
m_table->clear();
|
||||
m_table->setHorizontalHeaderLabels({tr("Active"), tr("Type"), tr("Function"), tr("Address"),
|
||||
tr("End Addr"), tr("Break"), tr("Log"), tr("Read"),
|
||||
tr("Write"), tr("Condition")});
|
||||
m_table->horizontalHeader()->setStretchLastSection(true);
|
||||
|
||||
// Get row height for icons
|
||||
m_table->setRowCount(1);
|
||||
const int height = m_table->rowHeight(0);
|
||||
|
||||
int i = 0;
|
||||
m_table->setRowCount(i);
|
||||
|
||||
// Create icon based on row height, downscaled for whitespace padding.
|
||||
const int downscale = static_cast<int>(0.8 * height);
|
||||
QPixmap enabled_icon =
|
||||
Resources::GetThemeIcon("debugger_breakpoint").pixmap(QSize(downscale, downscale));
|
||||
|
||||
const auto create_item = [](const QString& string = {}) {
|
||||
QTableWidgetItem* item = new QTableWidgetItem(string);
|
||||
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
||||
return item;
|
||||
};
|
||||
|
||||
QTableWidgetItem empty_item = QTableWidgetItem();
|
||||
empty_item.setFlags(Qt::NoItemFlags);
|
||||
QTableWidgetItem icon_item = QTableWidgetItem();
|
||||
icon_item.setData(Qt::DecorationRole, enabled_icon);
|
||||
QTableWidgetItem disabled_item = QTableWidgetItem();
|
||||
disabled_item.setFlags(Qt::NoItemFlags);
|
||||
|
||||
const QColor disabled_color =
|
||||
Settings::Instance().IsThemeDark() ? QColor(75, 75, 75) : QColor(225, 225, 225);
|
||||
disabled_item.setBackground(disabled_color);
|
||||
|
||||
auto& power_pc = m_system.GetPowerPC();
|
||||
auto& breakpoints = power_pc.GetBreakPoints();
|
||||
auto& memchecks = power_pc.GetMemChecks();
|
||||
|
@ -185,111 +311,114 @@ void BreakpointWidget::Update()
|
|||
{
|
||||
m_table->setRowCount(i + 1);
|
||||
|
||||
auto* active = create_item(bp.is_enabled ? tr("on") : tr("off"));
|
||||
auto* active = create_item();
|
||||
|
||||
active->setData(ADDRESS_ROLE, bp.address);
|
||||
active->setData(IS_MEMCHECK_ROLE, false);
|
||||
if (bp.is_enabled)
|
||||
active->setData(Qt::DecorationRole, enabled_icon);
|
||||
|
||||
m_table->setItem(i, 0, active);
|
||||
m_table->setItem(i, 1, create_item(QStringLiteral("BP")));
|
||||
m_table->setItem(i, ENABLED_COLUMN, active);
|
||||
m_table->setItem(i, TYPE_COLUMN, create_item(QStringLiteral("BP")));
|
||||
|
||||
auto* symbol_item = create_item();
|
||||
|
||||
if (const Common::Symbol* const symbol = ppc_symbol_db.GetSymbolFromAddr(bp.address))
|
||||
m_table->setItem(i, 2, create_item(QString::fromStdString(symbol->name)));
|
||||
symbol_item->setText(
|
||||
QString::fromStdString(symbol->name).simplified().remove(QStringLiteral(" ")));
|
||||
|
||||
m_table->setItem(i, 3,
|
||||
create_item(QStringLiteral("%1").arg(bp.address, 8, 16, QLatin1Char('0'))));
|
||||
m_table->setItem(i, SYMBOL_COLUMN, symbol_item);
|
||||
|
||||
QString flags;
|
||||
auto* address_item = create_item(QStringLiteral("%1").arg(bp.address, 8, 16, QLatin1Char('0')));
|
||||
address_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
|
||||
|
||||
if (bp.break_on_hit)
|
||||
flags.append(QLatin1Char{'b'});
|
||||
m_table->setItem(i, ADDRESS_COLUMN, address_item);
|
||||
m_table->setItem(i, BREAK_COLUMN, bp.break_on_hit ? icon_item.clone() : empty_item.clone());
|
||||
m_table->setItem(i, LOG_COLUMN, bp.log_on_hit ? icon_item.clone() : empty_item.clone());
|
||||
|
||||
if (bp.log_on_hit)
|
||||
flags.append(QLatin1Char{'l'});
|
||||
m_table->setItem(i, END_ADDRESS_COLUMN, disabled_item.clone());
|
||||
m_table->setItem(i, READ_COLUMN, disabled_item.clone());
|
||||
m_table->setItem(i, WRITE_COLUMN, disabled_item.clone());
|
||||
|
||||
m_table->setItem(i, 4, create_item(flags));
|
||||
m_table->setItem(
|
||||
i, CONDITION_COLUMN,
|
||||
create_item(bp.condition ? QString::fromStdString(bp.condition->GetText()) : QString()));
|
||||
|
||||
QString condition;
|
||||
|
||||
if (bp.condition)
|
||||
condition = QString::fromStdString(bp.condition->GetText());
|
||||
|
||||
m_table->setItem(i, 5, create_item(condition));
|
||||
// Color rows that are effectively disabled.
|
||||
if (!bp.is_enabled || (!bp.log_on_hit && !bp.break_on_hit))
|
||||
{
|
||||
for (int col = 0; col < m_table->columnCount(); col++)
|
||||
m_table->item(i, col)->setBackground(disabled_color);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
m_table->sortItems(ADDRESS_COLUMN);
|
||||
|
||||
// Memory Breakpoints
|
||||
for (const auto& mbp : memchecks.GetMemChecks())
|
||||
{
|
||||
m_table->setRowCount(i + 1);
|
||||
auto* active =
|
||||
create_item(mbp.is_enabled && (mbp.break_on_hit || mbp.log_on_hit) ? tr("on") : tr("off"));
|
||||
auto* active = create_item();
|
||||
active->setData(ADDRESS_ROLE, mbp.start_address);
|
||||
active->setData(IS_MEMCHECK_ROLE, true);
|
||||
if (mbp.is_enabled)
|
||||
active->setData(Qt::DecorationRole, enabled_icon);
|
||||
|
||||
m_table->setItem(i, 0, active);
|
||||
m_table->setItem(i, 1, create_item(QStringLiteral("MBP")));
|
||||
m_table->setItem(i, ENABLED_COLUMN, active);
|
||||
m_table->setItem(i, TYPE_COLUMN, create_item(QStringLiteral("MBP")));
|
||||
|
||||
auto* symbol_item = create_item();
|
||||
|
||||
if (const Common::Symbol* const symbol = ppc_symbol_db.GetSymbolFromAddr(mbp.start_address))
|
||||
m_table->setItem(i, 2, create_item(QString::fromStdString(symbol->name)));
|
||||
|
||||
if (mbp.is_ranged)
|
||||
{
|
||||
m_table->setItem(i, 3,
|
||||
create_item(QStringLiteral("%1 - %2")
|
||||
.arg(mbp.start_address, 8, 16, QLatin1Char('0'))
|
||||
.arg(mbp.end_address, 8, 16, QLatin1Char('0'))));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_table->setItem(
|
||||
i, 3, create_item(QStringLiteral("%1").arg(mbp.start_address, 8, 16, QLatin1Char('0'))));
|
||||
symbol_item->setText(
|
||||
QString::fromStdString(symbol->name).simplified().remove(QStringLiteral(" ")));
|
||||
}
|
||||
|
||||
QString flags;
|
||||
m_table->setItem(i, SYMBOL_COLUMN, symbol_item);
|
||||
|
||||
if (mbp.is_break_on_read)
|
||||
flags.append(QLatin1Char{'r'});
|
||||
auto* start_address_item =
|
||||
create_item(QStringLiteral("%1").arg(mbp.start_address, 8, 16, QLatin1Char('0')));
|
||||
start_address_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
|
||||
|
||||
if (mbp.is_break_on_write)
|
||||
flags.append(QLatin1Char{'w'});
|
||||
m_table->setItem(i, ADDRESS_COLUMN, start_address_item);
|
||||
|
||||
m_table->setItem(i, 4, create_item(flags));
|
||||
auto* end_address_item =
|
||||
create_item(QStringLiteral("%1").arg(mbp.end_address, 8, 16, QLatin1Char('0')));
|
||||
end_address_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
|
||||
end_address_item->setData(ADDRESS_ROLE, mbp.end_address);
|
||||
|
||||
QString condition;
|
||||
m_table->setItem(i, END_ADDRESS_COLUMN, end_address_item);
|
||||
|
||||
if (mbp.condition)
|
||||
condition = QString::fromStdString(mbp.condition->GetText());
|
||||
m_table->setItem(i, BREAK_COLUMN, mbp.break_on_hit ? icon_item.clone() : empty_item.clone());
|
||||
m_table->setItem(i, LOG_COLUMN, mbp.log_on_hit ? icon_item.clone() : empty_item.clone());
|
||||
m_table->setItem(i, READ_COLUMN, mbp.is_break_on_read ? icon_item.clone() : empty_item.clone());
|
||||
m_table->setItem(i, WRITE_COLUMN,
|
||||
mbp.is_break_on_write ? icon_item.clone() : empty_item.clone());
|
||||
|
||||
m_table->setItem(i, 5, create_item(condition));
|
||||
m_table->setItem(
|
||||
i, CONDITION_COLUMN,
|
||||
create_item(mbp.condition ? QString::fromStdString(mbp.condition->GetText()) : QString()));
|
||||
|
||||
// Color rows that are effectively disabled.
|
||||
if (!mbp.is_enabled || (!mbp.is_break_on_write && !mbp.is_break_on_read) ||
|
||||
(!mbp.break_on_hit && !mbp.log_on_hit))
|
||||
{
|
||||
for (int col = 0; col < m_table->columnCount(); col++)
|
||||
m_table->item(i, col)->setBackground(disabled_color);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpointWidget::OnDelete()
|
||||
{
|
||||
const auto selected_items = m_table->selectedItems();
|
||||
if (selected_items.empty())
|
||||
return;
|
||||
|
||||
const auto item = selected_items.constFirst();
|
||||
const auto address = item->data(ADDRESS_ROLE).toUInt();
|
||||
const bool is_memcheck = item->data(IS_MEMCHECK_ROLE).toBool();
|
||||
|
||||
if (is_memcheck)
|
||||
{
|
||||
const QSignalBlocker blocker(Settings::Instance());
|
||||
m_system.GetPowerPC().GetMemChecks().Remove(address);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_system.GetPowerPC().GetBreakPoints().Remove(address);
|
||||
}
|
||||
|
||||
emit BreakpointsChanged();
|
||||
Update();
|
||||
m_table->resizeColumnToContents(ENABLED_COLUMN);
|
||||
m_table->resizeColumnToContents(TYPE_COLUMN);
|
||||
m_table->resizeColumnToContents(BREAK_COLUMN);
|
||||
m_table->resizeColumnToContents(LOG_COLUMN);
|
||||
m_table->resizeColumnToContents(READ_COLUMN);
|
||||
m_table->resizeColumnToContents(WRITE_COLUMN);
|
||||
}
|
||||
|
||||
void BreakpointWidget::OnClear()
|
||||
|
@ -318,8 +447,8 @@ void BreakpointWidget::OnEditBreakpoint(u32 address, bool is_instruction_bp)
|
|||
{
|
||||
if (is_instruction_bp)
|
||||
{
|
||||
auto* dialog =
|
||||
new BreakpointDialog(this, m_system.GetPowerPC().GetBreakPoints().GetBreakpoint(address));
|
||||
auto* dialog = new BreakpointDialog(
|
||||
this, m_system.GetPowerPC().GetBreakPoints().GetRegularBreakpoint(address));
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
SetQWidgetWindowDecorations(dialog);
|
||||
dialog->exec();
|
||||
|
@ -377,15 +506,13 @@ void BreakpointWidget::OnSave()
|
|||
ini.Save(File::GetUserPath(D_GAMESETTINGS_IDX) + SConfig::GetInstance().GetGameID() + ".ini");
|
||||
}
|
||||
|
||||
void BreakpointWidget::OnContextMenu()
|
||||
void BreakpointWidget::OnContextMenu(const QPoint& pos)
|
||||
{
|
||||
const auto& selected_items = m_table->selectedItems();
|
||||
if (selected_items.isEmpty())
|
||||
{
|
||||
const auto row = m_table->rowAt(pos.y());
|
||||
const auto& selected_item = m_table->item(row, 0);
|
||||
if (selected_item == nullptr)
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& selected_item = selected_items.constFirst();
|
||||
const auto bp_address = static_cast<u32>(selected_item->data(ADDRESS_ROLE).toUInt());
|
||||
const auto is_memory_breakpoint = selected_item->data(IS_MEMCHECK_ROLE).toBool();
|
||||
|
||||
|
@ -402,9 +529,9 @@ void BreakpointWidget::OnContextMenu()
|
|||
return;
|
||||
|
||||
menu->addAction(tr("Show in Code"), [this, bp_address] { emit ShowCode(bp_address); });
|
||||
menu->addAction(bp_iter->is_enabled ? tr("Disable") : tr("Enable"), [this, &bp_address]() {
|
||||
m_system.GetPowerPC().GetBreakPoints().ToggleBreakPoint(bp_address);
|
||||
|
||||
menu->addAction(tr("Edit..."), [this, bp_address] { OnEditBreakpoint(bp_address, true); });
|
||||
menu->addAction(tr("Delete"), [this, &bp_address]() {
|
||||
m_system.GetPowerPC().GetBreakPoints().Remove(bp_address);
|
||||
emit BreakpointsChanged();
|
||||
Update();
|
||||
});
|
||||
|
@ -419,36 +546,114 @@ void BreakpointWidget::OnContextMenu()
|
|||
return;
|
||||
|
||||
menu->addAction(tr("Show in Memory"), [this, bp_address] { emit ShowMemory(bp_address); });
|
||||
menu->addAction(mb_iter->is_enabled ? tr("Disable") : tr("Enable"), [this, &bp_address]() {
|
||||
m_system.GetPowerPC().GetMemChecks().ToggleBreakPoint(bp_address);
|
||||
|
||||
menu->addAction(tr("Edit..."), [this, bp_address] { OnEditBreakpoint(bp_address, false); });
|
||||
menu->addAction(tr("Delete"), [this, &bp_address]() {
|
||||
const QSignalBlocker blocker(Settings::Instance());
|
||||
m_system.GetPowerPC().GetMemChecks().Remove(bp_address);
|
||||
emit BreakpointsChanged();
|
||||
Update();
|
||||
});
|
||||
}
|
||||
menu->addAction(tr("Edit..."), [this, bp_address, is_memory_breakpoint] {
|
||||
OnEditBreakpoint(bp_address, !is_memory_breakpoint);
|
||||
});
|
||||
|
||||
menu->exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void BreakpointWidget::AddBP(u32 addr)
|
||||
void BreakpointWidget::OnItemChanged(QTableWidgetItem* item)
|
||||
{
|
||||
AddBP(addr, false, true, true, {});
|
||||
if (item->column() != ADDRESS_COLUMN && item->column() != END_ADDRESS_COLUMN)
|
||||
return;
|
||||
|
||||
bool ok;
|
||||
const u32 new_address = item->text().toUInt(&ok, 16);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
const bool is_code_bp = !m_table->item(item->row(), 0)->data(IS_MEMCHECK_ROLE).toBool();
|
||||
const u32 base_address =
|
||||
static_cast<u32>(m_table->item(item->row(), 0)->data(ADDRESS_ROLE).toUInt());
|
||||
|
||||
if (is_code_bp)
|
||||
{
|
||||
if (item->column() != ADDRESS_COLUMN || new_address == base_address)
|
||||
return;
|
||||
|
||||
EditBreakpoint(base_address, item->column(), item->text());
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 end_address = static_cast<u32>(
|
||||
m_table->item(item->row(), END_ADDRESS_COLUMN)->data(ADDRESS_ROLE).toUInt());
|
||||
|
||||
// Need to check that the start/base address is always <= end_address.
|
||||
if ((item->column() == ADDRESS_COLUMN && new_address == base_address) ||
|
||||
(item->column() == END_ADDRESS_COLUMN && new_address == end_address))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((item->column() == ADDRESS_COLUMN && new_address <= end_address) ||
|
||||
(item->column() == END_ADDRESS_COLUMN && new_address >= base_address))
|
||||
{
|
||||
EditMBP(base_address, item->column(), item->text());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Removes invalid text from cell.
|
||||
Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpointWidget::AddBP(u32 addr, bool temp, bool break_on_hit, bool log_on_hit,
|
||||
const QString& condition)
|
||||
void BreakpointWidget::AddBP(u32 addr)
|
||||
{
|
||||
AddBP(addr, true, true, {});
|
||||
}
|
||||
|
||||
void BreakpointWidget::AddBP(u32 addr, bool break_on_hit, bool log_on_hit, const QString& condition)
|
||||
{
|
||||
m_system.GetPowerPC().GetBreakPoints().Add(
|
||||
addr, temp, break_on_hit, log_on_hit,
|
||||
addr, break_on_hit, log_on_hit,
|
||||
!condition.isEmpty() ? Expression::TryParse(condition.toUtf8().constData()) : std::nullopt);
|
||||
|
||||
emit BreakpointsChanged();
|
||||
Update();
|
||||
}
|
||||
|
||||
void BreakpointWidget::EditBreakpoint(u32 address, int edit, std::optional<QString> string)
|
||||
{
|
||||
TBreakPoint bp;
|
||||
const TBreakPoint* old_bp = m_system.GetPowerPC().GetBreakPoints().GetRegularBreakpoint(address);
|
||||
bp.is_enabled = edit == ENABLED_COLUMN ? !old_bp->is_enabled : old_bp->is_enabled;
|
||||
bp.log_on_hit = edit == LOG_COLUMN ? !old_bp->log_on_hit : old_bp->log_on_hit;
|
||||
bp.break_on_hit = edit == BREAK_COLUMN ? !old_bp->break_on_hit : old_bp->break_on_hit;
|
||||
|
||||
if (edit == ADDRESS_COLUMN && string.has_value())
|
||||
{
|
||||
bool ok;
|
||||
const u32 new_address = string.value().toUInt(&ok, 16);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
bp.address = new_address;
|
||||
}
|
||||
else
|
||||
{
|
||||
bp.address = address;
|
||||
}
|
||||
|
||||
if (edit == CONDITION_COLUMN && string.has_value())
|
||||
bp.condition = Expression::TryParse(string.value().toUtf8().constData());
|
||||
else if (old_bp->condition.has_value() && edit != CONDITION_COLUMN)
|
||||
bp.condition = Expression::TryParse(old_bp->condition.value().GetText());
|
||||
|
||||
// Unlike MBPs it Add() for TBreakpoint doesn't check to see if it already exists.
|
||||
m_system.GetPowerPC().GetBreakPoints().Remove(address);
|
||||
m_system.GetPowerPC().GetBreakPoints().Add(std::move(bp));
|
||||
|
||||
emit BreakpointsChanged();
|
||||
Update();
|
||||
}
|
||||
|
||||
void BreakpointWidget::AddAddressMBP(u32 addr, bool on_read, bool on_write, bool do_log,
|
||||
bool do_break, const QString& condition)
|
||||
{
|
||||
|
@ -494,3 +699,61 @@ void BreakpointWidget::AddRangedMBP(u32 from, u32 to, bool on_read, bool on_writ
|
|||
emit BreakpointsChanged();
|
||||
Update();
|
||||
}
|
||||
|
||||
void BreakpointWidget::EditMBP(u32 address, int edit, std::optional<QString> string)
|
||||
{
|
||||
bool address_changed = false;
|
||||
|
||||
TMemCheck mbp;
|
||||
const TMemCheck* old_mbp = m_system.GetPowerPC().GetMemChecks().GetMemCheck(address);
|
||||
mbp.is_enabled = edit == ENABLED_COLUMN ? !old_mbp->is_enabled : old_mbp->is_enabled;
|
||||
mbp.log_on_hit = edit == LOG_COLUMN ? !old_mbp->log_on_hit : old_mbp->log_on_hit;
|
||||
mbp.break_on_hit = edit == BREAK_COLUMN ? !old_mbp->break_on_hit : old_mbp->break_on_hit;
|
||||
mbp.is_break_on_read =
|
||||
edit == READ_COLUMN ? !old_mbp->is_break_on_read : old_mbp->is_break_on_read;
|
||||
mbp.is_break_on_write =
|
||||
edit == WRITE_COLUMN ? !old_mbp->is_break_on_write : old_mbp->is_break_on_write;
|
||||
|
||||
if ((edit == ADDRESS_COLUMN || edit == END_ADDRESS_COLUMN) && string.has_value())
|
||||
{
|
||||
bool ok;
|
||||
const u32 new_address = string.value().toUInt(&ok, 16);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
if (edit == ADDRESS_COLUMN)
|
||||
{
|
||||
mbp.start_address = new_address;
|
||||
mbp.end_address = old_mbp->end_address;
|
||||
address_changed = true;
|
||||
}
|
||||
else if (edit == END_ADDRESS_COLUMN)
|
||||
{
|
||||
// Will update existing mbp, so does not use address_changed bool.
|
||||
mbp.start_address = old_mbp->start_address;
|
||||
mbp.end_address = new_address;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mbp.start_address = old_mbp->start_address;
|
||||
mbp.end_address = old_mbp->end_address;
|
||||
}
|
||||
|
||||
mbp.is_ranged = mbp.start_address != mbp.end_address;
|
||||
|
||||
if (edit == CONDITION_COLUMN && string.has_value())
|
||||
mbp.condition = Expression::TryParse(string.value().toUtf8().constData());
|
||||
else if (old_mbp->condition.has_value() && edit != CONDITION_COLUMN)
|
||||
mbp.condition = Expression::TryParse(old_mbp->condition.value().GetText());
|
||||
|
||||
{
|
||||
const QSignalBlocker blocker(Settings::Instance());
|
||||
m_system.GetPowerPC().GetMemChecks().Add(std::move(mbp));
|
||||
if (address_changed)
|
||||
m_system.GetPowerPC().GetMemChecks().Remove(address);
|
||||
}
|
||||
|
||||
emit BreakpointsChanged();
|
||||
Update();
|
||||
}
|
||||
|
|
|
@ -3,20 +3,29 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QString>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class QAction;
|
||||
class QCloseEvent;
|
||||
class QPoint;
|
||||
class QShowEvent;
|
||||
class QTableWidget;
|
||||
class QTableWidgetItem;
|
||||
class QToolBar;
|
||||
class QWidget;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class System;
|
||||
}
|
||||
|
||||
class CustomDelegate;
|
||||
|
||||
class BreakpointWidget : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -25,7 +34,7 @@ public:
|
|||
~BreakpointWidget();
|
||||
|
||||
void AddBP(u32 addr);
|
||||
void AddBP(u32 addr, bool temp, bool break_on_hit, bool log_on_hit, const QString& condition);
|
||||
void AddBP(u32 addr, bool break_on_hit, bool log_on_hit, const QString& condition);
|
||||
void AddAddressMBP(u32 addr, bool on_read = true, bool on_write = true, bool do_log = true,
|
||||
bool do_break = true, const QString& condition = {});
|
||||
void AddRangedMBP(u32 from, u32 to, bool do_read = true, bool do_write = true, bool do_log = true,
|
||||
|
@ -45,14 +54,17 @@ protected:
|
|||
private:
|
||||
void CreateWidgets();
|
||||
|
||||
void OnDelete();
|
||||
void EditBreakpoint(u32 address, int edit, std::optional<QString> = std::nullopt);
|
||||
void EditMBP(u32 address, int edit, std::optional<QString> = std::nullopt);
|
||||
|
||||
void OnClear();
|
||||
void OnClicked(QTableWidgetItem* item);
|
||||
void OnNewBreakpoint();
|
||||
void OnEditBreakpoint(u32 address, bool is_instruction_bp);
|
||||
void OnLoad();
|
||||
void OnSave();
|
||||
void OnContextMenu();
|
||||
|
||||
void OnContextMenu(const QPoint& pos);
|
||||
void OnItemChanged(QTableWidgetItem* item);
|
||||
void UpdateIcons();
|
||||
|
||||
Core::System& m_system;
|
||||
|
@ -60,7 +72,6 @@ private:
|
|||
QToolBar* m_toolbar;
|
||||
QTableWidget* m_table;
|
||||
QAction* m_new;
|
||||
QAction* m_delete;
|
||||
QAction* m_clear;
|
||||
QAction* m_load;
|
||||
QAction* m_save;
|
||||
|
|
|
@ -156,6 +156,7 @@ CodeViewWidget::CodeViewWidget()
|
|||
horizontalHeader()->setStretchLastSection(true);
|
||||
setHorizontalHeaderItem(CODE_VIEW_COLUMN_BREAKPOINT, new QTableWidgetItem());
|
||||
setHorizontalHeaderItem(CODE_VIEW_COLUMN_ADDRESS, new QTableWidgetItem(tr("Address")));
|
||||
// i18n: Short for "Instruction"
|
||||
setHorizontalHeaderItem(CODE_VIEW_COLUMN_INSTRUCTION, new QTableWidgetItem(tr("Instr.")));
|
||||
setHorizontalHeaderItem(CODE_VIEW_COLUMN_PARAMETERS, new QTableWidgetItem(tr("Parameters")));
|
||||
setHorizontalHeaderItem(CODE_VIEW_COLUMN_DESCRIPTION, new QTableWidgetItem(tr("Symbols")));
|
||||
|
@ -382,10 +383,11 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
|
|||
if (ins == "blr")
|
||||
ins_item->setForeground(dark_theme ? QColor(0xa0FFa0) : Qt::darkGreen);
|
||||
|
||||
if (debug_interface.IsBreakpoint(addr))
|
||||
const TBreakPoint* bp = power_pc.GetBreakPoints().GetRegularBreakpoint(addr);
|
||||
if (bp != nullptr)
|
||||
{
|
||||
auto icon = Resources::GetThemeIcon("debugger_breakpoint").pixmap(QSize(rowh - 2, rowh - 2));
|
||||
if (!power_pc.GetBreakPoints().IsBreakPointEnable(addr))
|
||||
if (!bp->is_enabled)
|
||||
{
|
||||
QPixmap disabled_icon(icon.size());
|
||||
disabled_icon.fill(Qt::transparent);
|
||||
|
@ -568,44 +570,44 @@ void CodeViewWidget::OnContextMenu()
|
|||
const bool has_symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
auto* follow_branch_action =
|
||||
menu->addAction(tr("Follow &branch"), this, &CodeViewWidget::OnFollowBranch);
|
||||
menu->addAction(tr("Follow &Branch"), this, &CodeViewWidget::OnFollowBranch);
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
menu->addAction(tr("&Copy address"), this, &CodeViewWidget::OnCopyAddress);
|
||||
menu->addAction(tr("&Copy Address"), this, &CodeViewWidget::OnCopyAddress);
|
||||
auto* copy_address_action =
|
||||
menu->addAction(tr("Copy &function"), this, &CodeViewWidget::OnCopyFunction);
|
||||
menu->addAction(tr("Copy &Function"), this, &CodeViewWidget::OnCopyFunction);
|
||||
auto* copy_line_action =
|
||||
menu->addAction(tr("Copy code &line"), this, &CodeViewWidget::OnCopyCode);
|
||||
auto* copy_hex_action = menu->addAction(tr("Copy &hex"), this, &CodeViewWidget::OnCopyHex);
|
||||
menu->addAction(tr("Copy Code &Line"), this, &CodeViewWidget::OnCopyCode);
|
||||
auto* copy_hex_action = menu->addAction(tr("Copy &Hex"), this, &CodeViewWidget::OnCopyHex);
|
||||
|
||||
menu->addAction(tr("Show in &memory"), this, &CodeViewWidget::OnShowInMemory);
|
||||
menu->addAction(tr("Show in &Memory"), this, &CodeViewWidget::OnShowInMemory);
|
||||
auto* show_target_memory =
|
||||
menu->addAction(tr("Show target in memor&y"), this, &CodeViewWidget::OnShowTargetInMemory);
|
||||
menu->addAction(tr("Show Target in Memor&y"), this, &CodeViewWidget::OnShowTargetInMemory);
|
||||
auto* copy_target_memory =
|
||||
menu->addAction(tr("Copy tar&get address"), this, &CodeViewWidget::OnCopyTargetAddress);
|
||||
menu->addAction(tr("Copy Tar&get Address"), this, &CodeViewWidget::OnCopyTargetAddress);
|
||||
menu->addSeparator();
|
||||
|
||||
auto* symbol_rename_action =
|
||||
menu->addAction(tr("&Rename symbol"), this, &CodeViewWidget::OnRenameSymbol);
|
||||
menu->addAction(tr("&Rename Symbol"), this, &CodeViewWidget::OnRenameSymbol);
|
||||
auto* symbol_size_action =
|
||||
menu->addAction(tr("Set symbol &size"), this, &CodeViewWidget::OnSetSymbolSize);
|
||||
menu->addAction(tr("Set Symbol &Size"), this, &CodeViewWidget::OnSetSymbolSize);
|
||||
auto* symbol_end_action =
|
||||
menu->addAction(tr("Set symbol &end address"), this, &CodeViewWidget::OnSetSymbolEndAddress);
|
||||
menu->addAction(tr("Set Symbol &End Address"), this, &CodeViewWidget::OnSetSymbolEndAddress);
|
||||
menu->addSeparator();
|
||||
|
||||
menu->addAction(tr("Run &To Here"), this, &CodeViewWidget::OnRunToHere);
|
||||
auto* run_to_action = menu->addAction(tr("Run &to Here"), this, &CodeViewWidget::OnRunToHere);
|
||||
auto* function_action =
|
||||
menu->addAction(tr("&Add function"), this, &CodeViewWidget::OnAddFunction);
|
||||
menu->addAction(tr("&Add Function"), this, &CodeViewWidget::OnAddFunction);
|
||||
auto* ppc_action = menu->addAction(tr("PPC vs Host"), this, &CodeViewWidget::OnPPCComparison);
|
||||
auto* insert_blr_action = menu->addAction(tr("&Insert blr"), this, &CodeViewWidget::OnInsertBLR);
|
||||
auto* insert_nop_action = menu->addAction(tr("Insert &nop"), this, &CodeViewWidget::OnInsertNOP);
|
||||
auto* insert_blr_action = menu->addAction(tr("&Insert BLR"), this, &CodeViewWidget::OnInsertBLR);
|
||||
auto* insert_nop_action = menu->addAction(tr("Insert &NOP"), this, &CodeViewWidget::OnInsertNOP);
|
||||
auto* replace_action =
|
||||
menu->addAction(tr("Re&place instruction"), this, &CodeViewWidget::OnReplaceInstruction);
|
||||
menu->addAction(tr("Re&place Instruction"), this, &CodeViewWidget::OnReplaceInstruction);
|
||||
auto* assemble_action =
|
||||
menu->addAction(tr("Assemble instruction"), this, &CodeViewWidget::OnAssembleInstruction);
|
||||
menu->addAction(tr("Assemble Instruction"), this, &CodeViewWidget::OnAssembleInstruction);
|
||||
auto* restore_action =
|
||||
menu->addAction(tr("Restore instruction"), this, &CodeViewWidget::OnRestoreInstruction);
|
||||
menu->addAction(tr("Restore Instruction"), this, &CodeViewWidget::OnRestoreInstruction);
|
||||
|
||||
QString target;
|
||||
bool valid_load_store = false;
|
||||
|
@ -630,14 +632,14 @@ void CodeViewWidget::OnContextMenu()
|
|||
follow_branch_enabled = GetBranchFromAddress(guard, addr);
|
||||
}
|
||||
|
||||
auto* run_until_menu = menu->addMenu(tr("Run until (ignoring breakpoints)"));
|
||||
// i18n: One of the options shown below "Run until (ignoring breakpoints)"
|
||||
auto* run_until_menu = menu->addMenu(tr("Run Until (Ignoring Breakpoints)"));
|
||||
// i18n: One of the options shown below "Run Until (Ignoring Breakpoints)"
|
||||
run_until_menu->addAction(tr("%1's value is hit").arg(target), this,
|
||||
[this] { AutoStep(CodeTrace::AutoStop::Always); });
|
||||
// i18n: One of the options shown below "Run until (ignoring breakpoints)"
|
||||
// i18n: One of the options shown below "Run Until (Ignoring Breakpoints)"
|
||||
run_until_menu->addAction(tr("%1's value is used").arg(target), this,
|
||||
[this] { AutoStep(CodeTrace::AutoStop::Used); });
|
||||
// i18n: One of the options shown below "Run until (ignoring breakpoints)"
|
||||
// i18n: One of the options shown below "Run Until (Ignoring Breakpoints)"
|
||||
run_until_menu->addAction(tr("%1's value is changed").arg(target),
|
||||
[this] { AutoStep(CodeTrace::AutoStop::Changed); });
|
||||
|
||||
|
@ -645,8 +647,8 @@ void CodeViewWidget::OnContextMenu()
|
|||
follow_branch_action->setEnabled(follow_branch_enabled);
|
||||
|
||||
for (auto* action :
|
||||
{copy_address_action, copy_line_action, copy_hex_action, function_action, ppc_action,
|
||||
insert_blr_action, insert_nop_action, replace_action, assemble_action})
|
||||
{copy_address_action, copy_line_action, copy_hex_action, function_action, run_to_action,
|
||||
ppc_action, insert_blr_action, insert_nop_action, replace_action, assemble_action})
|
||||
{
|
||||
action->setEnabled(running);
|
||||
}
|
||||
|
@ -676,7 +678,7 @@ void CodeViewWidget::AutoStep(CodeTrace::AutoStop option)
|
|||
CodeTrace code_trace;
|
||||
bool repeat = false;
|
||||
|
||||
QMessageBox msgbox(QMessageBox::NoIcon, tr("Run until"), {}, QMessageBox::Cancel);
|
||||
QMessageBox msgbox(QMessageBox::NoIcon, tr("Run Until"), {}, QMessageBox::Cancel);
|
||||
QPushButton* run_button = msgbox.addButton(tr("Keep Running"), QMessageBox::AcceptRole);
|
||||
// Not sure if we want default to be cancel. Spacebar can let you quickly continue autostepping if
|
||||
// Yes.
|
||||
|
@ -726,7 +728,7 @@ void CodeViewWidget::AutoStep(CodeTrace::AutoStop option)
|
|||
|
||||
for (u32 i = 1; i <= 3; i++)
|
||||
{
|
||||
if (results.mem_tracked.count(address + i))
|
||||
if (results.mem_tracked.contains(address + i))
|
||||
iter++;
|
||||
else
|
||||
break;
|
||||
|
@ -869,9 +871,7 @@ void CodeViewWidget::OnRunToHere()
|
|||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
|
||||
m_system.GetPowerPC().GetDebugInterface().SetBreakpoint(addr);
|
||||
m_system.GetPowerPC().GetDebugInterface().RunToBreakpoint();
|
||||
Update();
|
||||
m_system.GetPowerPC().GetDebugInterface().RunTo(addr);
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnPPCComparison()
|
||||
|
@ -931,7 +931,7 @@ void CodeViewWidget::OnRenameSymbol()
|
|||
|
||||
bool good;
|
||||
const QString name =
|
||||
QInputDialog::getText(this, tr("Rename symbol"), tr("Symbol name:"), QLineEdit::Normal,
|
||||
QInputDialog::getText(this, tr("Rename Symbol"), tr("Symbol Name:"), QLineEdit::Normal,
|
||||
QString::fromStdString(symbol->name), &good, Qt::WindowCloseButtonHint);
|
||||
|
||||
if (good && !name.isEmpty())
|
||||
|
@ -964,10 +964,9 @@ void CodeViewWidget::OnSetSymbolSize()
|
|||
return;
|
||||
|
||||
bool good;
|
||||
const int size =
|
||||
QInputDialog::getInt(this, tr("Rename symbol"),
|
||||
tr("Set symbol size (%1):").arg(QString::fromStdString(symbol->name)),
|
||||
symbol->size, 1, 0xFFFF, 1, &good, Qt::WindowCloseButtonHint);
|
||||
const int size = QInputDialog::getInt(
|
||||
this, tr("Rename Symbol"), tr("Symbol Size (%1):").arg(QString::fromStdString(symbol->name)),
|
||||
symbol->size, 1, 0xFFFF, 1, &good, Qt::WindowCloseButtonHint);
|
||||
|
||||
if (!good)
|
||||
return;
|
||||
|
@ -989,8 +988,8 @@ void CodeViewWidget::OnSetSymbolEndAddress()
|
|||
|
||||
bool good;
|
||||
const QString name = QInputDialog::getText(
|
||||
this, tr("Set symbol end address"),
|
||||
tr("Symbol (%1) end address:").arg(QString::fromStdString(symbol->name)), QLineEdit::Normal,
|
||||
this, tr("Set Symbol End Address"),
|
||||
tr("Symbol End Address (%1):").arg(QString::fromStdString(symbol->name)), QLineEdit::Normal,
|
||||
QStringLiteral("%1").arg(addr + symbol->size, 8, 16, QLatin1Char('0')), &good,
|
||||
Qt::WindowCloseButtonHint);
|
||||
|
||||
|
@ -1137,11 +1136,7 @@ void CodeViewWidget::showEvent(QShowEvent* event)
|
|||
|
||||
void CodeViewWidget::ToggleBreakpoint()
|
||||
{
|
||||
auto& power_pc = m_system.GetPowerPC();
|
||||
if (power_pc.GetDebugInterface().IsBreakpoint(GetContextAddress()))
|
||||
power_pc.GetBreakPoints().Remove(GetContextAddress());
|
||||
else
|
||||
power_pc.GetBreakPoints().Add(GetContextAddress());
|
||||
m_system.GetPowerPC().GetBreakPoints().ToggleBreakPoint(GetContextAddress());
|
||||
|
||||
emit BreakpointsChanged();
|
||||
Update();
|
||||
|
|
|
@ -206,6 +206,7 @@ void CodeWidget::OnBranchWatchDialog()
|
|||
m_branch_watch_dialog = new BranchWatchDialog(m_system, m_system.GetPowerPC().GetBranchWatch(),
|
||||
m_ppc_symbol_db, this, this);
|
||||
}
|
||||
SetQWidgetWindowDecorations(m_branch_watch_dialog);
|
||||
m_branch_watch_dialog->show();
|
||||
m_branch_watch_dialog->raise();
|
||||
m_branch_watch_dialog->activateWindow();
|
||||
|
@ -454,7 +455,6 @@ void CodeWidget::Step()
|
|||
auto& power_pc = m_system.GetPowerPC();
|
||||
PowerPC::CoreMode old_mode = power_pc.GetMode();
|
||||
power_pc.SetMode(PowerPC::CoreMode::Interpreter);
|
||||
power_pc.GetBreakPoints().ClearAllTemporary();
|
||||
cpu.StepOpcode(&sync_event);
|
||||
sync_event.WaitFor(std::chrono::milliseconds(20));
|
||||
power_pc.SetMode(old_mode);
|
||||
|
@ -481,9 +481,8 @@ void CodeWidget::StepOver()
|
|||
if (inst.LK)
|
||||
{
|
||||
auto& breakpoints = m_system.GetPowerPC().GetBreakPoints();
|
||||
breakpoints.ClearAllTemporary();
|
||||
breakpoints.Add(m_system.GetPPCState().pc + 4, true);
|
||||
cpu.EnableStepping(false);
|
||||
breakpoints.SetTemporary(m_system.GetPPCState().pc + 4);
|
||||
cpu.SetStepping(false);
|
||||
Core::DisplayMessage(tr("Step over in progress...").toStdString(), 2000);
|
||||
}
|
||||
else
|
||||
|
@ -518,12 +517,9 @@ void CodeWidget::StepOut()
|
|||
|
||||
auto& power_pc = m_system.GetPowerPC();
|
||||
auto& ppc_state = power_pc.GetPPCState();
|
||||
auto& breakpoints = power_pc.GetBreakPoints();
|
||||
{
|
||||
Core::CPUThreadGuard guard(m_system);
|
||||
|
||||
breakpoints.ClearAllTemporary();
|
||||
|
||||
PowerPC::CoreMode old_mode = power_pc.GetMode();
|
||||
power_pc.SetMode(PowerPC::CoreMode::Interpreter);
|
||||
|
||||
|
@ -546,8 +542,7 @@ void CodeWidget::StepOut()
|
|||
do
|
||||
{
|
||||
power_pc.SingleStep();
|
||||
} while (ppc_state.pc != next_pc && clock::now() < timeout &&
|
||||
!breakpoints.IsAddressBreakPoint(ppc_state.pc));
|
||||
} while (ppc_state.pc != next_pc && clock::now() < timeout && !power_pc.CheckBreakPoints());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -555,14 +550,14 @@ void CodeWidget::StepOut()
|
|||
}
|
||||
|
||||
inst = PowerPC::MMU::HostRead_Instruction(guard, ppc_state.pc);
|
||||
} while (clock::now() < timeout && !breakpoints.IsAddressBreakPoint(ppc_state.pc));
|
||||
} while (clock::now() < timeout && !power_pc.CheckBreakPoints());
|
||||
|
||||
power_pc.SetMode(old_mode);
|
||||
}
|
||||
|
||||
emit Host::GetInstance()->UpdateDisasmDialog();
|
||||
|
||||
if (breakpoints.IsAddressBreakPoint(ppc_state.pc))
|
||||
if (power_pc.CheckBreakPoints())
|
||||
Core::DisplayMessage(tr("Breakpoint encountered! Step out aborted.").toStdString(), 2000);
|
||||
else if (clock::now() >= timeout)
|
||||
Core::DisplayMessage(tr("Step out timed out!").toStdString(), 2000);
|
||||
|
|
|
@ -227,7 +227,7 @@ void NetworkWidget::ConnectWidgets()
|
|||
connect(m_dump_bba_checkbox, &QCheckBox::stateChanged, [](int state) {
|
||||
Config::SetBaseOrCurrent(Config::MAIN_NETWORK_DUMP_BBA, state == Qt::Checked);
|
||||
});
|
||||
connect(m_open_dump_folder, &QPushButton::pressed, [] {
|
||||
connect(m_open_dump_folder, &QPushButton::clicked, [] {
|
||||
const std::string location = File::GetUserPath(D_DUMPSSL_IDX);
|
||||
const QUrl url = QUrl::fromLocalFile(QString::fromStdString(location));
|
||||
QDesktopServices::openUrl(url);
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Core
|
|||
{
|
||||
class CPUThreadGuard;
|
||||
class System;
|
||||
}; // namespace Core
|
||||
} // namespace Core
|
||||
|
||||
class WatchWidget : public QDockWidget
|
||||
{
|
||||
|
|
|
@ -13,8 +13,5 @@
|
|||
<!-- This is needed to use adhoc signed linked libraries -->
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<!-- Allows the Steam overlay library to be injected into our process -->
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
<!-- This is needed to use adhoc signed linked libraries -->
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<!-- Allows the Steam overlay library to be injected into our process -->
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
<!-- This is needed to attach a debugger to the process -->
|
||||
<key>com.apple.security.get-task-allow</key>
|
||||
<true/>
|
||||
|
|
|
@ -214,7 +214,7 @@ void FIFOPlayerWindow::AddDescriptions()
|
|||
|
||||
void FIFOPlayerWindow::LoadRecording()
|
||||
{
|
||||
QString path = DolphinFileDialog::getOpenFileName(this, tr("Open FIFO log"), QString(),
|
||||
QString path = DolphinFileDialog::getOpenFileName(this, tr("Open FIFO Log"), QString(),
|
||||
tr("Dolphin FIFO Log (*.dff)"));
|
||||
|
||||
if (path.isEmpty())
|
||||
|
@ -225,7 +225,7 @@ void FIFOPlayerWindow::LoadRecording()
|
|||
|
||||
void FIFOPlayerWindow::SaveRecording()
|
||||
{
|
||||
QString path = DolphinFileDialog::getSaveFileName(this, tr("Save FIFO log"), QString(),
|
||||
QString path = DolphinFileDialog::getSaveFileName(this, tr("Save FIFO Log"), QString(),
|
||||
tr("Dolphin FIFO Log (*.dff)"));
|
||||
|
||||
if (path.isEmpty())
|
||||
|
|
|
@ -65,7 +65,7 @@ Slot OtherSlot(Slot slot)
|
|||
{
|
||||
return slot == Slot::A ? Slot::B : Slot::A;
|
||||
}
|
||||
}; // namespace
|
||||
} // namespace
|
||||
|
||||
struct GCMemcardManager::IconAnimationData
|
||||
{
|
||||
|
@ -356,8 +356,8 @@ void GCMemcardManager::SetSlotFileInteractive(Slot slot)
|
|||
{
|
||||
QString path = QDir::toNativeSeparators(
|
||||
DolphinFileDialog::getOpenFileName(this,
|
||||
slot == Slot::A ? tr("Set memory card file for Slot A") :
|
||||
tr("Set memory card file for Slot B"),
|
||||
slot == Slot::A ? tr("Set Memory Card File for Slot A") :
|
||||
tr("Set Memory Card File for Slot B"),
|
||||
QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)),
|
||||
QStringLiteral("%1 (*.raw *.gcp);;%2 (*)")
|
||||
.arg(tr("GameCube Memory Cards"), tr("All Files"))));
|
||||
|
|
|
@ -299,16 +299,14 @@ void GameList::MakeEmptyView()
|
|||
size_policy.setRetainSizeWhenHidden(true);
|
||||
m_empty->setSizePolicy(size_policy);
|
||||
|
||||
connect(&Settings::Instance(), &Settings::GameListRefreshRequested, this,
|
||||
[this, refreshing_msg = refreshing_msg] {
|
||||
m_empty->setText(refreshing_msg);
|
||||
m_empty->setEnabled(false);
|
||||
});
|
||||
connect(&Settings::Instance(), &Settings::GameListRefreshCompleted, this,
|
||||
[this, empty_msg = empty_msg] {
|
||||
m_empty->setText(empty_msg);
|
||||
m_empty->setEnabled(true);
|
||||
});
|
||||
connect(&Settings::Instance(), &Settings::GameListRefreshRequested, this, [this, refreshing_msg] {
|
||||
m_empty->setText(refreshing_msg);
|
||||
m_empty->setEnabled(false);
|
||||
});
|
||||
connect(&Settings::Instance(), &Settings::GameListRefreshCompleted, this, [this, empty_msg] {
|
||||
m_empty->setText(empty_msg);
|
||||
m_empty->setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
void GameList::resizeEvent(QResizeEvent* event)
|
||||
|
@ -805,15 +803,11 @@ bool GameList::AddShortcutToDesktop()
|
|||
|
||||
std::string game_name = game->GetName(Core::TitleDatabase());
|
||||
// Sanitize the string by removing all characters that cannot be used in NTFS file names
|
||||
game_name.erase(std::remove_if(game_name.begin(), game_name.end(),
|
||||
[](char ch) {
|
||||
static constexpr char illegal_characters[] = {
|
||||
'<', '>', ':', '\"', '/', '\\', '|', '?', '*'};
|
||||
return std::find(std::begin(illegal_characters),
|
||||
std::end(illegal_characters),
|
||||
ch) != std::end(illegal_characters);
|
||||
}),
|
||||
game_name.end());
|
||||
std::erase_if(game_name, [](char ch) {
|
||||
static constexpr char illegal_characters[] = {'<', '>', ':', '\"', '/', '\\', '|', '?', '*'};
|
||||
return std::find(std::begin(illegal_characters), std::end(illegal_characters), ch) !=
|
||||
std::end(illegal_characters);
|
||||
});
|
||||
|
||||
std::wstring desktop_path = std::wstring(desktop.get()) + UTF8ToTStr("\\" + game_name + ".lnk");
|
||||
auto persist_file = shell_link.try_query<IPersistFile>();
|
||||
|
|
|
@ -162,6 +162,11 @@ bool Host::GetGBAFocus()
|
|||
#endif
|
||||
}
|
||||
|
||||
bool Host::GetTASInputFocus() const
|
||||
{
|
||||
return m_tas_input_focus;
|
||||
}
|
||||
|
||||
bool Host::GetRenderFullscreen()
|
||||
{
|
||||
return m_render_fullscreen;
|
||||
|
@ -177,6 +182,11 @@ void Host::SetRenderFullscreen(bool fullscreen)
|
|||
}
|
||||
}
|
||||
|
||||
void Host::SetTASInputFocus(const bool focus)
|
||||
{
|
||||
m_tas_input_focus = focus;
|
||||
}
|
||||
|
||||
void Host::ResizeSurface(int new_width, int new_height)
|
||||
{
|
||||
if (g_presenter)
|
||||
|
@ -228,6 +238,11 @@ bool Host_RendererIsFullscreen()
|
|||
return Host::GetInstance()->GetRenderFullscreen();
|
||||
}
|
||||
|
||||
bool Host_TASInputHasFocus()
|
||||
{
|
||||
return Host::GetInstance()->GetTASInputFocus();
|
||||
}
|
||||
|
||||
void Host_YieldToUI()
|
||||
{
|
||||
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
|
@ -235,6 +250,9 @@ void Host_YieldToUI()
|
|||
|
||||
void Host_UpdateDisasmDialog()
|
||||
{
|
||||
if (Settings::Instance().GetIsContinuouslyFrameStepping())
|
||||
return;
|
||||
|
||||
QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->UpdateDisasmDialog(); });
|
||||
}
|
||||
|
||||
|
|
|
@ -25,12 +25,14 @@ public:
|
|||
bool GetRenderFullFocus();
|
||||
bool GetRenderFullscreen();
|
||||
bool GetGBAFocus();
|
||||
bool GetTASInputFocus() const;
|
||||
|
||||
void SetMainWindowHandle(void* handle);
|
||||
void SetRenderHandle(void* handle);
|
||||
void SetRenderFocus(bool focus);
|
||||
void SetRenderFullFocus(bool focus);
|
||||
void SetRenderFullscreen(bool fullscreen);
|
||||
void SetTASInputFocus(bool focus);
|
||||
void ResizeSurface(int new_width, int new_height);
|
||||
|
||||
signals:
|
||||
|
@ -49,4 +51,5 @@ private:
|
|||
std::atomic<bool> m_render_focus{false};
|
||||
std::atomic<bool> m_render_full_focus{false};
|
||||
std::atomic<bool> m_render_fullscreen{false};
|
||||
std::atomic<bool> m_tas_input_focus{false};
|
||||
};
|
||||
|
|
|
@ -113,6 +113,8 @@ static void HandleFrameStepHotkeys()
|
|||
|
||||
if ((frame_step_count == 0 || frame_step_count == FRAME_STEP_DELAY) && !frame_step_hold)
|
||||
{
|
||||
if (frame_step_count > 0)
|
||||
Settings::Instance().SetIsContinuouslyFrameStepping(true);
|
||||
Core::QueueHostJob([](auto& system) { Core::DoFrameStep(system); });
|
||||
frame_step_hold = true;
|
||||
}
|
||||
|
@ -138,6 +140,8 @@ static void HandleFrameStepHotkeys()
|
|||
frame_step_count = 0;
|
||||
frame_step_hold = false;
|
||||
frame_step_delay_count = 0;
|
||||
Settings::Instance().SetIsContinuouslyFrameStepping(false);
|
||||
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +163,8 @@ void HotkeyScheduler::Run()
|
|||
if (!HotkeyManagerEmu::IsEnabled())
|
||||
continue;
|
||||
|
||||
if (Core::GetState(Core::System::GetInstance()) != Core::State::Stopping)
|
||||
Core::System& system = Core::System::GetInstance();
|
||||
if (Core::GetState(system) != Core::State::Stopping)
|
||||
{
|
||||
// Obey window focus (config permitting) before checking hotkeys.
|
||||
Core::UpdateInputGate(Config::Get(Config::MAIN_FOCUSED_HOTKEYS));
|
||||
|
@ -187,7 +192,12 @@ void HotkeyScheduler::Run()
|
|||
if (IsHotkey(HK_EXIT))
|
||||
emit ExitHotkey();
|
||||
|
||||
if (!Core::IsRunningAndStarted())
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (IsHotkey(HK_OPEN_ACHIEVEMENTS))
|
||||
emit OpenAchievements();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
if (!Core::IsRunning(system))
|
||||
{
|
||||
// Only check for Play Recording hotkey when no game is running
|
||||
if (IsHotkey(HK_PLAY_RECORDING))
|
||||
|
@ -472,9 +482,14 @@ void HotkeyScheduler::Run()
|
|||
|
||||
auto ShowEmulationSpeed = []() {
|
||||
const float emulation_speed = Config::Get(Config::MAIN_EMULATION_SPEED);
|
||||
OSD::AddMessage(emulation_speed <= 0 ?
|
||||
"Speed Limit: Unlimited" :
|
||||
fmt::format("Speed Limit: {}%", std::lround(emulation_speed * 100.f)));
|
||||
if (!AchievementManager::GetInstance().IsHardcoreModeActive() ||
|
||||
Config::Get(Config::MAIN_EMULATION_SPEED) >= 1.0f ||
|
||||
Config::Get(Config::MAIN_EMULATION_SPEED) <= 0.0f)
|
||||
{
|
||||
OSD::AddMessage(emulation_speed <= 0 ? "Speed Limit: Unlimited" :
|
||||
fmt::format("Speed Limit: {}%",
|
||||
std::lround(emulation_speed * 100.f)));
|
||||
}
|
||||
};
|
||||
|
||||
if (IsHotkey(HK_DECREASE_EMULATION_SPEED))
|
||||
|
@ -589,15 +604,12 @@ void HotkeyScheduler::Run()
|
|||
{
|
||||
const bool new_value = !Config::Get(Config::FREE_LOOK_ENABLED);
|
||||
Config::SetCurrent(Config::FREE_LOOK_ENABLED, new_value);
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
const bool hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
if (hardcore)
|
||||
OSD::AddMessage("Free Look is Disabled in Hardcore Mode");
|
||||
else
|
||||
OSD::AddMessage(fmt::format("Free Look: {}", new_value ? "Enabled" : "Disabled"));
|
||||
#else // USE_RETRO_ACHIEVEMENTS
|
||||
OSD::AddMessage(fmt::format("Free Look: {}", new_value ? "Enabled" : "Disabled"));
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
}
|
||||
|
||||
// Savestates
|
||||
|
|
|
@ -53,6 +53,9 @@ signals:
|
|||
void ExportRecording();
|
||||
void ToggleReadOnlyMode();
|
||||
void ConnectWiiRemote(int id);
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
void OpenAchievements();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
void Step();
|
||||
void StepOver();
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
// static variable to ensure we open at the most recent figure file location
|
||||
static QString s_last_figure_path;
|
||||
|
||||
using FigureUIPosition = IOS::HLE::USB::FigureUIPosition;
|
||||
|
||||
InfinityBaseWindow::InfinityBaseWindow(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
// i18n: Window for managing Disney Infinity figures
|
||||
|
@ -48,7 +50,7 @@ InfinityBaseWindow::InfinityBaseWindow(QWidget* parent) : QWidget(parent)
|
|||
installEventFilter(this);
|
||||
|
||||
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()));
|
||||
};
|
||||
}
|
||||
|
||||
InfinityBaseWindow::~InfinityBaseWindow() = default;
|
||||
|
||||
|
@ -77,19 +79,23 @@ void InfinityBaseWindow::CreateMainWindow()
|
|||
auto* vbox_group = new QVBoxLayout();
|
||||
auto* scroll_area = new QScrollArea();
|
||||
|
||||
AddFigureSlot(vbox_group, tr("Play Set/Power Disc"), 0);
|
||||
AddFigureSlot(vbox_group, tr("Play Set/Power Disc"), FigureUIPosition::HexagonDiscOne);
|
||||
add_line(vbox_group);
|
||||
AddFigureSlot(vbox_group, tr("Player One"), 1);
|
||||
AddFigureSlot(vbox_group, tr("Power Disc Two"), FigureUIPosition::HexagonDiscTwo);
|
||||
add_line(vbox_group);
|
||||
AddFigureSlot(vbox_group, tr("Player One Ability One"), 3);
|
||||
AddFigureSlot(vbox_group, tr("Power Disc Three"), FigureUIPosition::HexagonDiscThree);
|
||||
add_line(vbox_group);
|
||||
AddFigureSlot(vbox_group, tr("Player One Ability Two"), 5);
|
||||
AddFigureSlot(vbox_group, tr("Player One"), FigureUIPosition::PlayerOne);
|
||||
add_line(vbox_group);
|
||||
AddFigureSlot(vbox_group, tr("Player Two"), 2);
|
||||
AddFigureSlot(vbox_group, tr("Player One Ability One"), FigureUIPosition::P1AbilityOne);
|
||||
add_line(vbox_group);
|
||||
AddFigureSlot(vbox_group, tr("Player Two Ability One"), 4);
|
||||
AddFigureSlot(vbox_group, tr("Player One Ability Two"), FigureUIPosition::P1AbilityTwo);
|
||||
add_line(vbox_group);
|
||||
AddFigureSlot(vbox_group, tr("Player Two Ability Two"), 6);
|
||||
AddFigureSlot(vbox_group, tr("Player Two"), FigureUIPosition::PlayerTwo);
|
||||
add_line(vbox_group);
|
||||
AddFigureSlot(vbox_group, tr("Player Two Ability One"), FigureUIPosition::P2AbilityOne);
|
||||
add_line(vbox_group);
|
||||
AddFigureSlot(vbox_group, tr("Player Two Ability Two"), FigureUIPosition::P2AbilityTwo);
|
||||
|
||||
m_group_figures->setLayout(vbox_group);
|
||||
scroll_area->setWidget(m_group_figures);
|
||||
|
@ -99,7 +105,7 @@ void InfinityBaseWindow::CreateMainWindow()
|
|||
setLayout(main_layout);
|
||||
}
|
||||
|
||||
void InfinityBaseWindow::AddFigureSlot(QVBoxLayout* vbox_group, QString name, u8 slot)
|
||||
void InfinityBaseWindow::AddFigureSlot(QVBoxLayout* vbox_group, QString name, FigureUIPosition slot)
|
||||
{
|
||||
auto* hbox_infinity = new QHBoxLayout();
|
||||
|
||||
|
@ -108,16 +114,16 @@ void InfinityBaseWindow::AddFigureSlot(QVBoxLayout* vbox_group, QString name, u8
|
|||
auto* clear_btn = new QPushButton(tr("Clear"));
|
||||
auto* create_btn = new QPushButton(tr("Create"));
|
||||
auto* load_btn = new QPushButton(tr("Load"));
|
||||
m_edit_figures[slot] = new QLineEdit();
|
||||
m_edit_figures[slot]->setEnabled(false);
|
||||
m_edit_figures[slot]->setText(tr("None"));
|
||||
m_edit_figures[static_cast<u8>(slot)] = new QLineEdit();
|
||||
m_edit_figures[static_cast<u8>(slot)]->setEnabled(false);
|
||||
m_edit_figures[static_cast<u8>(slot)]->setText(tr("None"));
|
||||
|
||||
connect(clear_btn, &QAbstractButton::clicked, this, [this, slot] { ClearFigure(slot); });
|
||||
connect(create_btn, &QAbstractButton::clicked, this, [this, slot] { CreateFigure(slot); });
|
||||
connect(load_btn, &QAbstractButton::clicked, this, [this, slot] { LoadFigure(slot); });
|
||||
|
||||
hbox_infinity->addWidget(label_skyname);
|
||||
hbox_infinity->addWidget(m_edit_figures[slot]);
|
||||
hbox_infinity->addWidget(m_edit_figures[static_cast<u8>(slot)]);
|
||||
hbox_infinity->addWidget(clear_btn);
|
||||
hbox_infinity->addWidget(create_btn);
|
||||
hbox_infinity->addWidget(load_btn);
|
||||
|
@ -125,15 +131,15 @@ void InfinityBaseWindow::AddFigureSlot(QVBoxLayout* vbox_group, QString name, u8
|
|||
vbox_group->addLayout(hbox_infinity);
|
||||
}
|
||||
|
||||
void InfinityBaseWindow::ClearFigure(u8 slot)
|
||||
void InfinityBaseWindow::ClearFigure(FigureUIPosition slot)
|
||||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
m_edit_figures[slot]->setText(tr("None"));
|
||||
m_edit_figures[static_cast<u8>(slot)]->setText(tr("None"));
|
||||
|
||||
system.GetInfinityBase().RemoveFigure(slot);
|
||||
}
|
||||
|
||||
void InfinityBaseWindow::LoadFigure(u8 slot)
|
||||
void InfinityBaseWindow::LoadFigure(FigureUIPosition slot)
|
||||
{
|
||||
const QString file_path =
|
||||
DolphinFileDialog::getOpenFileName(this, tr("Select Figure File"), s_last_figure_path,
|
||||
|
@ -148,7 +154,7 @@ void InfinityBaseWindow::LoadFigure(u8 slot)
|
|||
LoadFigurePath(slot, file_path);
|
||||
}
|
||||
|
||||
void InfinityBaseWindow::CreateFigure(u8 slot)
|
||||
void InfinityBaseWindow::CreateFigure(FigureUIPosition slot)
|
||||
{
|
||||
CreateFigureDialog create_dlg(this, slot);
|
||||
SetQWidgetWindowDecorations(&create_dlg);
|
||||
|
@ -158,7 +164,7 @@ void InfinityBaseWindow::CreateFigure(u8 slot)
|
|||
}
|
||||
}
|
||||
|
||||
void InfinityBaseWindow::LoadFigurePath(u8 slot, const QString& path)
|
||||
void InfinityBaseWindow::LoadFigurePath(FigureUIPosition slot, const QString& path)
|
||||
{
|
||||
File::IOFile inf_file(path.toStdString(), "r+b");
|
||||
if (!inf_file)
|
||||
|
@ -183,11 +189,11 @@ void InfinityBaseWindow::LoadFigurePath(u8 slot, const QString& path)
|
|||
auto& system = Core::System::GetInstance();
|
||||
|
||||
system.GetInfinityBase().RemoveFigure(slot);
|
||||
m_edit_figures[slot]->setText(QString::fromStdString(
|
||||
m_edit_figures[static_cast<u8>(slot)]->setText(QString::fromStdString(
|
||||
system.GetInfinityBase().LoadFigure(file_data, std::move(inf_file), slot)));
|
||||
}
|
||||
|
||||
CreateFigureDialog::CreateFigureDialog(QWidget* parent, u8 slot) : QDialog(parent)
|
||||
CreateFigureDialog::CreateFigureDialog(QWidget* parent, FigureUIPosition slot) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Infinity Figure Creator"));
|
||||
setObjectName(QStringLiteral("infinity_creator"));
|
||||
|
@ -201,10 +207,14 @@ CreateFigureDialog::CreateFigureDialog(QWidget* parent, u8 slot) : QDialog(paren
|
|||
{
|
||||
const auto figure = entry.second;
|
||||
// Only display entry if it is a piece appropriate for the slot
|
||||
if ((slot == 0 &&
|
||||
if ((slot == FigureUIPosition::HexagonDiscOne &&
|
||||
((figure > 0x1E8480 && figure < 0x2DC6BF) || (figure > 0x3D0900 && figure < 0x4C4B3F))) ||
|
||||
((slot == 1 || slot == 2) && figure < 0x1E847F) ||
|
||||
((slot == 3 || slot == 4 || slot == 5 || slot == 6) &&
|
||||
((slot == FigureUIPosition::HexagonDiscTwo || slot == FigureUIPosition::HexagonDiscThree) &&
|
||||
(figure > 0x3D0900 && figure < 0x4C4B3F)) ||
|
||||
((slot == FigureUIPosition::PlayerOne || slot == FigureUIPosition::PlayerTwo) &&
|
||||
figure < 0x1E847F) ||
|
||||
((slot == FigureUIPosition::P1AbilityOne || slot == FigureUIPosition::P1AbilityTwo ||
|
||||
slot == FigureUIPosition::P2AbilityOne || slot == FigureUIPosition::P2AbilityTwo) &&
|
||||
(figure > 0x2DC6C0 && figure < 0x3D08FF)))
|
||||
{
|
||||
const auto figure_name = QString::fromStdString(entry.first);
|
||||
|
|
|
@ -20,6 +20,11 @@ namespace Core
|
|||
enum class State;
|
||||
}
|
||||
|
||||
namespace IOS::HLE::USB
|
||||
{
|
||||
enum class FigureUIPosition : u8;
|
||||
}
|
||||
|
||||
class InfinityBaseWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -28,17 +33,17 @@ public:
|
|||
~InfinityBaseWindow() override;
|
||||
|
||||
protected:
|
||||
std::array<QLineEdit*, 7> m_edit_figures;
|
||||
std::array<QLineEdit*, 9> m_edit_figures;
|
||||
|
||||
private:
|
||||
void CreateMainWindow();
|
||||
void AddFigureSlot(QVBoxLayout* vbox_group, QString name, u8 slot);
|
||||
void AddFigureSlot(QVBoxLayout* vbox_group, QString name, IOS::HLE::USB::FigureUIPosition slot);
|
||||
void OnEmulationStateChanged(Core::State state);
|
||||
void EmulateBase(bool emulate);
|
||||
void ClearFigure(u8 slot);
|
||||
void LoadFigure(u8 slot);
|
||||
void CreateFigure(u8 slot);
|
||||
void LoadFigurePath(u8 slot, const QString& path);
|
||||
void ClearFigure(IOS::HLE::USB::FigureUIPosition slot);
|
||||
void LoadFigure(IOS::HLE::USB::FigureUIPosition slot);
|
||||
void CreateFigure(IOS::HLE::USB::FigureUIPosition slot);
|
||||
void LoadFigurePath(IOS::HLE::USB::FigureUIPosition slot, const QString& path);
|
||||
|
||||
QCheckBox* m_checkbox;
|
||||
QGroupBox* m_group_figures;
|
||||
|
@ -49,7 +54,7 @@ class CreateFigureDialog : public QDialog
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CreateFigureDialog(QWidget* parent, u8 slot);
|
||||
explicit CreateFigureDialog(QWidget* parent, IOS::HLE::USB::FigureUIPosition slot);
|
||||
QString GetFilePath() const;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Dolphin</string>
|
||||
<string>DolphinQt</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>Dolphin.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
|
|
@ -259,7 +259,6 @@ int main(int argc, char* argv[])
|
|||
Settings::Instance().ApplyStyle();
|
||||
|
||||
MainWindow win{std::move(boot), static_cast<const char*>(options.get("movie"))};
|
||||
win.Show();
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -229,8 +229,6 @@ MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters,
|
|||
setAcceptDrops(true);
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
|
||||
InitControllers();
|
||||
|
||||
CreateComponents();
|
||||
|
||||
ConnectGameList();
|
||||
|
@ -239,6 +237,17 @@ MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters,
|
|||
ConnectRenderWidget();
|
||||
ConnectStack();
|
||||
ConnectMenuBar();
|
||||
|
||||
QSettings& settings = Settings::GetQSettings();
|
||||
restoreState(settings.value(QStringLiteral("mainwindow/state")).toByteArray());
|
||||
restoreGeometry(settings.value(QStringLiteral("mainwindow/geometry")).toByteArray());
|
||||
if (!Settings::Instance().IsBatchModeEnabled())
|
||||
{
|
||||
SetQWidgetWindowDecorations(this);
|
||||
show();
|
||||
}
|
||||
|
||||
InitControllers();
|
||||
ConnectHotkeys();
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
||||
|
@ -291,11 +300,6 @@ MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters,
|
|||
m_state_slot =
|
||||
std::clamp(Settings::Instance().GetStateSlot(), 1, static_cast<int>(State::NUM_STATES));
|
||||
|
||||
QSettings& settings = Settings::GetQSettings();
|
||||
|
||||
restoreState(settings.value(QStringLiteral("mainwindow/state")).toByteArray());
|
||||
restoreGeometry(settings.value(QStringLiteral("mainwindow/geometry")).toByteArray());
|
||||
|
||||
m_render_widget_geometry = settings.value(QStringLiteral("renderwidget/geometry")).toByteArray();
|
||||
|
||||
// Restoring of window states can sometimes go wrong, resulting in widgets being visible when they
|
||||
|
@ -321,6 +325,12 @@ MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters,
|
|||
}
|
||||
|
||||
Host::GetInstance()->SetMainWindowHandle(reinterpret_cast<void*>(winId()));
|
||||
|
||||
if (m_pending_boot != nullptr)
|
||||
{
|
||||
StartGame(std::move(m_pending_boot));
|
||||
m_pending_boot.reset();
|
||||
}
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
|
@ -347,8 +357,11 @@ MainWindow::~MainWindow()
|
|||
|
||||
QSettings& settings = Settings::GetQSettings();
|
||||
|
||||
settings.setValue(QStringLiteral("mainwindow/state"), saveState());
|
||||
settings.setValue(QStringLiteral("mainwindow/geometry"), saveGeometry());
|
||||
if (!Settings::Instance().IsBatchModeEnabled())
|
||||
{
|
||||
settings.setValue(QStringLiteral("mainwindow/state"), saveState());
|
||||
settings.setValue(QStringLiteral("mainwindow/geometry"), saveGeometry());
|
||||
}
|
||||
|
||||
settings.setValue(QStringLiteral("renderwidget/geometry"), m_render_widget_geometry);
|
||||
|
||||
|
@ -645,6 +658,10 @@ void MainWindow::ConnectHotkeys()
|
|||
movie.SetReadOnly(read_only);
|
||||
emit ReadOnlyModeChanged(read_only);
|
||||
});
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
connect(m_hotkey_scheduler, &HotkeyScheduler::OpenAchievements, this,
|
||||
&MainWindow::ShowAchievementsWindow, Qt::QueuedConnection);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
connect(m_hotkey_scheduler, &HotkeyScheduler::Step, m_code_widget, &CodeWidget::Step);
|
||||
connect(m_hotkey_scheduler, &HotkeyScheduler::StepOver, m_code_widget, &CodeWidget::StepOver);
|
||||
|
@ -1910,7 +1927,7 @@ void MainWindow::OnImportNANDBackup()
|
|||
return;
|
||||
|
||||
QString file =
|
||||
DolphinFileDialog::getOpenFileName(this, tr("Select the save file"), QDir::currentPath(),
|
||||
DolphinFileDialog::getOpenFileName(this, tr("Select NAND Backup"), QDir::currentPath(),
|
||||
tr("BootMii NAND backup file (*.bin);;"
|
||||
"All Files (*)"));
|
||||
|
||||
|
@ -1936,7 +1953,7 @@ void MainWindow::OnImportNANDBackup()
|
|||
[this] {
|
||||
std::optional<std::string> keys_file = RunOnObject(this, [this] {
|
||||
return DolphinFileDialog::getOpenFileName(
|
||||
this, tr("Select the keys file (OTP/SEEPROM dump)"), QDir::currentPath(),
|
||||
this, tr("Select Keys File (OTP/SEEPROM Dump)"), QDir::currentPath(),
|
||||
tr("BootMii keys file (*.bin);;"
|
||||
"All Files (*)"))
|
||||
.toStdString();
|
||||
|
@ -1985,7 +2002,7 @@ void MainWindow::OnStartRecording()
|
|||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& movie = system.GetMovie();
|
||||
if ((!Core::IsRunningAndStarted() && Core::IsRunning(system)) || movie.IsRecordingInput() ||
|
||||
if (Core::GetState(system) == Core::State::Starting || movie.IsRecordingInput() ||
|
||||
movie.IsPlayingInput())
|
||||
{
|
||||
return;
|
||||
|
@ -2175,19 +2192,3 @@ void MainWindow::ShowRiivolutionBootWidget(const UICommon::GameFile& game)
|
|||
AddRiivolutionPatches(boot_params.get(), std::move(w.GetPatches()));
|
||||
StartGame(std::move(boot_params));
|
||||
}
|
||||
|
||||
void MainWindow::Show()
|
||||
{
|
||||
if (!Settings::Instance().IsBatchModeEnabled())
|
||||
{
|
||||
SetQWidgetWindowDecorations(this);
|
||||
QWidget::show();
|
||||
}
|
||||
|
||||
// If the booting of a game was requested on start up, do that now
|
||||
if (m_pending_boot != nullptr)
|
||||
{
|
||||
StartGame(std::move(m_pending_boot));
|
||||
m_pending_boot.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,6 @@ public:
|
|||
const std::string& movie_path);
|
||||
~MainWindow();
|
||||
|
||||
void Show();
|
||||
WindowSystemInfo GetWindowSystemInfo() const;
|
||||
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
|
|
@ -128,14 +128,9 @@ void MenuBar::OnEmulationStateChanged(Core::State state)
|
|||
m_screenshot_action->setEnabled(running);
|
||||
m_state_save_menu->setEnabled(running);
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
const bool hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
m_state_load_menu->setEnabled(running && !hardcore);
|
||||
m_frame_advance_action->setEnabled(running && !hardcore);
|
||||
#else // USE_RETRO_ACHIEVEMENTS
|
||||
m_state_load_menu->setEnabled(running);
|
||||
m_frame_advance_action->setEnabled(running);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
// Movie
|
||||
m_recording_read_only->setEnabled(running);
|
||||
|
@ -145,11 +140,7 @@ void MenuBar::OnEmulationStateChanged(Core::State state)
|
|||
m_recording_export->setEnabled(false);
|
||||
}
|
||||
m_recording_play->setEnabled(m_game_selected && !running);
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
m_recording_play->setEnabled(m_game_selected && !running && !hardcore);
|
||||
#else // USE_RETRO_ACHIEVEMENTS
|
||||
m_recording_play->setEnabled(m_game_selected && !running);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
m_recording_start->setEnabled((m_game_selected || running) &&
|
||||
!Core::System::GetInstance().GetMovie().IsPlayingInput());
|
||||
|
||||
|
@ -272,12 +263,9 @@ void MenuBar::AddToolsMenu()
|
|||
tools_menu->addSeparator();
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (Config::Get(Config::RA_ENABLED))
|
||||
{
|
||||
tools_menu->addAction(tr("Achievements"), this, [this] { emit ShowAchievementsWindow(); });
|
||||
tools_menu->addAction(tr("Achievements"), this, [this] { emit ShowAchievementsWindow(); });
|
||||
|
||||
tools_menu->addSeparator();
|
||||
}
|
||||
tools_menu->addSeparator();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
QMenu* gc_ipl = tools_menu->addMenu(tr("Load GameCube Main Menu"));
|
||||
|
@ -416,6 +404,10 @@ void MenuBar::AddStateSlotMenu(QMenu* emu_menu)
|
|||
action->setChecked(true);
|
||||
|
||||
connect(action, &QAction::triggered, this, [=, this]() { emit SetStateSlot(i); });
|
||||
connect(this, &MenuBar::SetStateSlot, [action, i](const int slot) {
|
||||
if (slot == i)
|
||||
action->setChecked(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1088,8 +1080,8 @@ void MenuBar::UpdateToolsMenu(bool emulation_started)
|
|||
|
||||
void MenuBar::InstallWAD()
|
||||
{
|
||||
QString wad_file = DolphinFileDialog::getOpenFileName(
|
||||
this, tr("Select a title to install to NAND"), QString(), tr("WAD files (*.wad)"));
|
||||
QString wad_file = DolphinFileDialog::getOpenFileName(this, tr("Select Title to Install to NAND"),
|
||||
QString(), tr("WAD files (*.wad)"));
|
||||
|
||||
if (wad_file.isEmpty())
|
||||
return;
|
||||
|
@ -1109,7 +1101,7 @@ void MenuBar::InstallWAD()
|
|||
void MenuBar::ImportWiiSave()
|
||||
{
|
||||
QString file =
|
||||
DolphinFileDialog::getOpenFileName(this, tr("Select the save file"), QDir::currentPath(),
|
||||
DolphinFileDialog::getOpenFileName(this, tr("Select Save File"), QDir::currentPath(),
|
||||
tr("Wii save files (*.bin);;"
|
||||
"All Files (*)"));
|
||||
|
||||
|
@ -1577,7 +1569,7 @@ void MenuBar::SaveSymbolMap()
|
|||
void MenuBar::LoadOtherSymbolMap()
|
||||
{
|
||||
const QString file = DolphinFileDialog::getOpenFileName(
|
||||
this, tr("Load map file"), QString::fromStdString(File::GetUserPath(D_MAPS_IDX)),
|
||||
this, tr("Load Map File"), QString::fromStdString(File::GetUserPath(D_MAPS_IDX)),
|
||||
tr("Dolphin Map File (*.map)"));
|
||||
|
||||
if (file.isEmpty())
|
||||
|
@ -1594,7 +1586,7 @@ void MenuBar::LoadOtherSymbolMap()
|
|||
void MenuBar::LoadBadSymbolMap()
|
||||
{
|
||||
const QString file = DolphinFileDialog::getOpenFileName(
|
||||
this, tr("Load map file"), QString::fromStdString(File::GetUserPath(D_MAPS_IDX)),
|
||||
this, tr("Load Map File"), QString::fromStdString(File::GetUserPath(D_MAPS_IDX)),
|
||||
tr("Dolphin Map File (*.map)"));
|
||||
|
||||
if (file.isEmpty())
|
||||
|
@ -1612,7 +1604,7 @@ void MenuBar::SaveSymbolMapAs()
|
|||
{
|
||||
const std::string& title_id_str = SConfig::GetInstance().m_debugger_game_id;
|
||||
const QString file = DolphinFileDialog::getSaveFileName(
|
||||
this, tr("Save map file"),
|
||||
this, tr("Save Map File"),
|
||||
QString::fromStdString(File::GetUserPath(D_MAPS_IDX) + "/" + title_id_str + ".map"),
|
||||
tr("Dolphin Map File (*.map)"));
|
||||
|
||||
|
@ -1668,7 +1660,7 @@ void MenuBar::CreateSignatureFile()
|
|||
this, tr("Input"), tr("Only export symbols with prefix:\n(Blank for all symbols)"),
|
||||
QLineEdit::Normal, QString{}, nullptr, Qt::WindowCloseButtonHint);
|
||||
|
||||
const QString file = DolphinFileDialog::getSaveFileName(this, tr("Save signature file"),
|
||||
const QString file = DolphinFileDialog::getSaveFileName(this, tr("Save Signature File"),
|
||||
QDir::homePath(), GetSignatureSelector());
|
||||
if (file.isEmpty())
|
||||
return;
|
||||
|
@ -1693,7 +1685,7 @@ void MenuBar::AppendSignatureFile()
|
|||
this, tr("Input"), tr("Only append symbols with prefix:\n(Blank for all symbols)"),
|
||||
QLineEdit::Normal, QString{}, nullptr, Qt::WindowCloseButtonHint);
|
||||
|
||||
const QString file = DolphinFileDialog::getSaveFileName(this, tr("Append signature to"),
|
||||
const QString file = DolphinFileDialog::getSaveFileName(this, tr("Append Signature To"),
|
||||
QDir::homePath(), GetSignatureSelector());
|
||||
if (file.isEmpty())
|
||||
return;
|
||||
|
@ -1716,7 +1708,7 @@ void MenuBar::AppendSignatureFile()
|
|||
|
||||
void MenuBar::ApplySignatureFile()
|
||||
{
|
||||
const QString file = DolphinFileDialog::getOpenFileName(this, tr("Apply signature file"),
|
||||
const QString file = DolphinFileDialog::getOpenFileName(this, tr("Apply Signature File"),
|
||||
QDir::homePath(), GetSignatureSelector());
|
||||
|
||||
if (file.isEmpty())
|
||||
|
@ -1736,17 +1728,17 @@ void MenuBar::ApplySignatureFile()
|
|||
void MenuBar::CombineSignatureFiles()
|
||||
{
|
||||
const QString priorityFile = DolphinFileDialog::getOpenFileName(
|
||||
this, tr("Choose priority input file"), QDir::homePath(), GetSignatureSelector());
|
||||
this, tr("Choose Priority Input File"), QDir::homePath(), GetSignatureSelector());
|
||||
if (priorityFile.isEmpty())
|
||||
return;
|
||||
|
||||
const QString secondaryFile = DolphinFileDialog::getOpenFileName(
|
||||
this, tr("Choose secondary input file"), QDir::homePath(), GetSignatureSelector());
|
||||
this, tr("Choose Secondary Input File"), QDir::homePath(), GetSignatureSelector());
|
||||
if (secondaryFile.isEmpty())
|
||||
return;
|
||||
|
||||
const QString saveFile = DolphinFileDialog::getSaveFileName(
|
||||
this, tr("Save combined output file as"), QDir::homePath(), GetSignatureSelector());
|
||||
this, tr("Save Combined Output File As"), QDir::homePath(), GetSignatureSelector());
|
||||
if (saveFile.isEmpty())
|
||||
return;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ enum class State;
|
|||
namespace DiscIO
|
||||
{
|
||||
enum class Region;
|
||||
};
|
||||
}
|
||||
|
||||
namespace UICommon
|
||||
{
|
||||
|
|
|
@ -125,7 +125,7 @@ void ChunkedProgressDialog::SetProgress(const int pid, const u64 progress)
|
|||
{
|
||||
QString player_name = GetPlayerNameFromPID(pid);
|
||||
|
||||
if (!m_status_labels.count(pid))
|
||||
if (!m_status_labels.contains(pid))
|
||||
return;
|
||||
|
||||
const float acquired = progress / 1024.0f / 1024.0f;
|
||||
|
|
|
@ -122,7 +122,7 @@ void GameDigestDialog::SetProgress(int pid, int progress)
|
|||
{
|
||||
QString player_name = GetPlayerNameFromPID(pid);
|
||||
|
||||
if (!m_status_labels.count(pid))
|
||||
if (!m_status_labels.contains(pid))
|
||||
return;
|
||||
|
||||
m_status_labels[pid]->setText(
|
||||
|
@ -134,7 +134,7 @@ void GameDigestDialog::SetResult(int pid, const std::string& result)
|
|||
{
|
||||
QString player_name = GetPlayerNameFromPID(pid);
|
||||
|
||||
if (!m_status_labels.count(pid))
|
||||
if (!m_status_labels.contains(pid))
|
||||
return;
|
||||
|
||||
m_status_labels[pid]->setText(
|
||||
|
|
|
@ -665,7 +665,7 @@ void NetPlayDialog::UpdateGUI()
|
|||
|
||||
auto* name_item = new QTableWidgetItem(QString::fromStdString(p->name));
|
||||
name_item->setToolTip(name_item->text());
|
||||
const auto& status_info = player_status.count(p->game_status) ?
|
||||
const auto& status_info = player_status.contains(p->game_status) ?
|
||||
player_status.at(p->game_status) :
|
||||
std::make_pair(QStringLiteral("?"), QStringLiteral("?"));
|
||||
auto* status_item = new QTableWidgetItem(status_info.first);
|
||||
|
|
|
@ -17,7 +17,7 @@ void ClearLayoutRecursively(QLayout* layout)
|
|||
if (child->widget())
|
||||
{
|
||||
layout->removeWidget(child->widget());
|
||||
delete child->widget();
|
||||
child->widget()->deleteLater();
|
||||
}
|
||||
else if (child->layout())
|
||||
{
|
||||
|
|
|
@ -504,7 +504,7 @@ bool RenderWidget::event(QEvent* event)
|
|||
|
||||
void RenderWidget::PassEventToPresenter(const QEvent* event)
|
||||
{
|
||||
if (!Core::IsRunningAndStarted())
|
||||
if (!Core::IsRunning(Core::System::GetInstance()))
|
||||
return;
|
||||
|
||||
switch (event->type())
|
||||
|
|
|
@ -59,7 +59,12 @@ Settings::Settings()
|
|||
{
|
||||
qRegisterMetaType<Core::State>();
|
||||
Core::AddOnStateChangedCallback([this](Core::State new_state) {
|
||||
QueueOnObject(this, [this, new_state] { emit EmulationStateChanged(new_state); });
|
||||
QueueOnObject(this, [this, new_state] {
|
||||
// Avoid signal spam while continuously frame stepping. Will still send a signal for the first
|
||||
// and last framestep.
|
||||
if (!m_continuously_frame_stepping)
|
||||
emit EmulationStateChanged(new_state);
|
||||
});
|
||||
});
|
||||
|
||||
Config::AddConfigChangedCallback([this] {
|
||||
|
@ -118,9 +123,8 @@ QSettings& Settings::GetQSettings()
|
|||
return settings;
|
||||
}
|
||||
|
||||
void Settings::SetThemeName(const QString& theme_name)
|
||||
void Settings::TriggerThemeChanged()
|
||||
{
|
||||
Config::SetBaseOrCurrent(Config::MAIN_THEME_NAME, theme_name.toStdString());
|
||||
emit ThemeChanged();
|
||||
}
|
||||
|
||||
|
@ -355,11 +359,6 @@ void Settings::NotifyRefreshGameListComplete()
|
|||
emit GameListRefreshCompleted();
|
||||
}
|
||||
|
||||
void Settings::RefreshMetadata()
|
||||
{
|
||||
emit MetadataRefreshRequested();
|
||||
}
|
||||
|
||||
void Settings::NotifyMetadataRefreshComplete()
|
||||
{
|
||||
emit MetadataRefreshCompleted();
|
||||
|
@ -419,23 +418,11 @@ void Settings::SetStateSlot(int slot)
|
|||
GetQSettings().setValue(QStringLiteral("Emulation/StateSlot"), slot);
|
||||
}
|
||||
|
||||
void Settings::SetCursorVisibility(Config::ShowCursor hideCursor)
|
||||
{
|
||||
Config::SetBaseOrCurrent(Config::MAIN_SHOW_CURSOR, hideCursor);
|
||||
emit CursorVisibilityChanged();
|
||||
}
|
||||
|
||||
Config::ShowCursor Settings::GetCursorVisibility() const
|
||||
{
|
||||
return Config::Get(Config::MAIN_SHOW_CURSOR);
|
||||
}
|
||||
|
||||
void Settings::SetLockCursor(bool lock_cursor)
|
||||
{
|
||||
Config::SetBaseOrCurrent(Config::MAIN_LOCK_CURSOR, lock_cursor);
|
||||
emit LockCursorChanged();
|
||||
}
|
||||
|
||||
bool Settings::GetLockCursor() const
|
||||
{
|
||||
return Config::Get(Config::MAIN_LOCK_CURSOR);
|
||||
|
@ -446,7 +433,6 @@ void Settings::SetKeepWindowOnTop(bool top)
|
|||
if (IsKeepWindowOnTopEnabled() == top)
|
||||
return;
|
||||
|
||||
Config::SetBaseOrCurrent(Config::MAIN_KEEP_WINDOW_ON_TOP, top);
|
||||
emit KeepWindowOnTopChanged(top);
|
||||
}
|
||||
|
||||
|
@ -553,21 +539,10 @@ bool Settings::GetCheatsEnabled() const
|
|||
return Config::Get(Config::MAIN_ENABLE_CHEATS);
|
||||
}
|
||||
|
||||
void Settings::SetCheatsEnabled(bool enabled)
|
||||
{
|
||||
if (Config::Get(Config::MAIN_ENABLE_CHEATS) != enabled)
|
||||
{
|
||||
Config::SetBaseOrCurrent(Config::MAIN_ENABLE_CHEATS, enabled);
|
||||
emit EnableCheatsChanged(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::SetDebugModeEnabled(bool enabled)
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
enabled = false;
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
if (IsDebugModeEnabled() != enabled)
|
||||
{
|
||||
Config::SetBaseOrCurrent(Config::MAIN_ENABLE_DEBUGGING, enabled);
|
||||
|
@ -848,3 +823,13 @@ void Settings::SetUSBKeyboardConnected(bool connected)
|
|||
emit USBKeyboardConnectionChanged(connected);
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::SetIsContinuouslyFrameStepping(bool is_stepping)
|
||||
{
|
||||
m_continuously_frame_stepping = is_stepping;
|
||||
}
|
||||
|
||||
bool Settings::GetIsContinuouslyFrameStepping() const
|
||||
{
|
||||
return m_continuously_frame_stepping;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
static QSettings& GetQSettings();
|
||||
|
||||
// UI
|
||||
void SetThemeName(const QString& theme_name);
|
||||
void TriggerThemeChanged();
|
||||
void InitDefaultPalette();
|
||||
void UpdateSystemDark();
|
||||
void SetSystemDark(bool dark);
|
||||
|
@ -104,7 +104,6 @@ public:
|
|||
void RefreshGameList();
|
||||
void NotifyRefreshGameListStarted();
|
||||
void NotifyRefreshGameListComplete();
|
||||
void RefreshMetadata();
|
||||
void NotifyMetadataRefreshComplete();
|
||||
void ReloadTitleDB();
|
||||
bool IsAutoRefreshEnabled() const;
|
||||
|
@ -121,10 +120,11 @@ public:
|
|||
bool IsUSBKeyboardConnected() const;
|
||||
void SetUSBKeyboardConnected(bool connected);
|
||||
|
||||
void SetIsContinuouslyFrameStepping(bool is_stepping);
|
||||
bool GetIsContinuouslyFrameStepping() const;
|
||||
|
||||
// Graphics
|
||||
void SetCursorVisibility(Config::ShowCursor hideCursor);
|
||||
Config::ShowCursor GetCursorVisibility() const;
|
||||
void SetLockCursor(bool lock_cursor);
|
||||
bool GetLockCursor() const;
|
||||
void SetKeepWindowOnTop(bool top);
|
||||
bool IsKeepWindowOnTopEnabled() const;
|
||||
|
@ -145,7 +145,6 @@ public:
|
|||
|
||||
// Cheats
|
||||
bool GetCheatsEnabled() const;
|
||||
void SetCheatsEnabled(bool enabled);
|
||||
|
||||
// Debug
|
||||
void SetDebugModeEnabled(bool enabled);
|
||||
|
@ -226,11 +225,14 @@ signals:
|
|||
void SDCardInsertionChanged(bool inserted);
|
||||
void USBKeyboardConnectionChanged(bool connected);
|
||||
void EnableGfxModsChanged(bool enabled);
|
||||
void HardcoreStateChanged();
|
||||
|
||||
private:
|
||||
Settings();
|
||||
|
||||
bool m_batch = false;
|
||||
std::atomic<bool> m_continuously_frame_stepping = false;
|
||||
|
||||
std::shared_ptr<NetPlay::NetPlayClient> m_client;
|
||||
std::shared_ptr<NetPlay::NetPlayServer> m_server;
|
||||
ControllerInterface::HotplugCallbackHandle m_hotplug_callback_handle;
|
||||
|
|
|
@ -100,7 +100,7 @@ void AdvancedPane::CreateLayout()
|
|||
clock_override_layout->addLayout(cpu_clock_override_slider_layout);
|
||||
|
||||
m_cpu_clock_override_slider = new QSlider(Qt::Horizontal);
|
||||
m_cpu_clock_override_slider->setRange(0, 150);
|
||||
m_cpu_clock_override_slider->setRange(1, 400);
|
||||
cpu_clock_override_slider_layout->addWidget(m_cpu_clock_override_slider);
|
||||
|
||||
m_cpu_clock_override_slider_label = new QLabel();
|
||||
|
@ -203,8 +203,7 @@ void AdvancedPane::ConnectLayout()
|
|||
});
|
||||
|
||||
connect(m_cpu_clock_override_slider, &QSlider::valueChanged, [this](int oc_factor) {
|
||||
// Vaguely exponential scaling?
|
||||
const float factor = std::exp2f((m_cpu_clock_override_slider->value() - 100.f) / 25.f);
|
||||
const float factor = m_cpu_clock_override_slider->value() / 100.f;
|
||||
Config::SetBaseOrCurrent(Config::MAIN_OVERCLOCK, factor);
|
||||
Update();
|
||||
});
|
||||
|
@ -271,8 +270,8 @@ void AdvancedPane::Update()
|
|||
|
||||
{
|
||||
const QSignalBlocker blocker(m_cpu_clock_override_slider);
|
||||
m_cpu_clock_override_slider->setValue(static_cast<int>(
|
||||
std::round(std::log2f(Config::Get(Config::MAIN_OVERCLOCK)) * 25.f + 100.f)));
|
||||
m_cpu_clock_override_slider->setValue(
|
||||
static_cast<int>(std::round(Config::Get(Config::MAIN_OVERCLOCK) * 100.f)));
|
||||
}
|
||||
|
||||
m_cpu_clock_override_slider_label->setText([] {
|
||||
|
|
|
@ -434,7 +434,7 @@ void GameCubePane::BrowseMemcard(ExpansionInterface::Slot slot)
|
|||
ASSERT(ExpansionInterface::IsMemcardSlot(slot));
|
||||
|
||||
const QString filename = DolphinFileDialog::getSaveFileName(
|
||||
this, tr("Choose a file to open or create"),
|
||||
this, tr("Choose a File to Open or Create"),
|
||||
QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)),
|
||||
tr("GameCube Memory Cards (*.raw *.gcp)"), nullptr, QFileDialog::DontConfirmOverwrite);
|
||||
|
||||
|
@ -538,8 +538,7 @@ void GameCubePane::BrowseGCIFolder(ExpansionInterface::Slot slot)
|
|||
ASSERT(ExpansionInterface::IsMemcardSlot(slot));
|
||||
|
||||
const QString path = DolphinFileDialog::getExistingDirectory(
|
||||
this, tr("Choose the GCI base folder"),
|
||||
QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)));
|
||||
this, tr("Choose GCI Base Folder"), QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)));
|
||||
|
||||
if (!path.isEmpty())
|
||||
SetGCIFolder(slot, path);
|
||||
|
@ -645,7 +644,7 @@ void GameCubePane::BrowseAGPRom(ExpansionInterface::Slot slot)
|
|||
ASSERT(ExpansionInterface::IsMemcardSlot(slot));
|
||||
|
||||
QString filename = DolphinFileDialog::getSaveFileName(
|
||||
this, tr("Choose a file to open"), QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)),
|
||||
this, tr("Choose a File to Open"), QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)),
|
||||
tr("Game Boy Advance Carts (*.gba)"), nullptr, QFileDialog::DontConfirmOverwrite);
|
||||
|
||||
if (!filename.isEmpty())
|
||||
|
|
|
@ -24,8 +24,11 @@
|
|||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
|
||||
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
|
||||
#include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h"
|
||||
#include "DolphinQt/Config/ToolTipControls/ToolTipPushButton.h"
|
||||
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
||||
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
|
||||
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
|
||||
#include "DolphinQt/QtUtils/SignalBlocking.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
@ -52,6 +55,7 @@ GeneralPane::GeneralPane(QWidget* parent) : QWidget(parent)
|
|||
{
|
||||
CreateLayout();
|
||||
LoadConfig();
|
||||
AddDescriptions();
|
||||
|
||||
ConnectLayout();
|
||||
|
||||
|
@ -81,14 +85,10 @@ void GeneralPane::CreateLayout()
|
|||
void GeneralPane::OnEmulationStateChanged(Core::State state)
|
||||
{
|
||||
const bool running = state != Core::State::Uninitialized;
|
||||
const bool hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
|
||||
m_checkbox_dualcore->setEnabled(!running);
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
bool hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
m_checkbox_cheats->setEnabled(!running && !hardcore);
|
||||
#else // USE_RETRO_ACHIEVEMENTS
|
||||
m_checkbox_cheats->setEnabled(!running);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
m_checkbox_override_region_settings->setEnabled(!running);
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
m_checkbox_discord_presence->setEnabled(!running);
|
||||
|
@ -134,17 +134,18 @@ void GeneralPane::CreateBasic()
|
|||
basic_group->setLayout(basic_group_layout);
|
||||
m_main_layout->addWidget(basic_group);
|
||||
|
||||
m_checkbox_dualcore = new QCheckBox(tr("Enable Dual Core (speedup)"));
|
||||
m_checkbox_dualcore = new ConfigBool(tr("Enable Dual Core (speedhack)"), Config::MAIN_CPU_THREAD);
|
||||
basic_group_layout->addWidget(m_checkbox_dualcore);
|
||||
|
||||
m_checkbox_override_region_settings = new QCheckBox(tr("Allow Mismatched Region Settings"));
|
||||
basic_group_layout->addWidget(m_checkbox_override_region_settings);
|
||||
|
||||
m_checkbox_auto_disc_change = new QCheckBox(tr("Change Discs Automatically"));
|
||||
m_checkbox_auto_disc_change =
|
||||
new ConfigBool(tr("Change Discs Automatically"), Config::MAIN_AUTO_DISC_CHANGE);
|
||||
basic_group_layout->addWidget(m_checkbox_auto_disc_change);
|
||||
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
m_checkbox_discord_presence = new QCheckBox(tr("Show Current Game on Discord"));
|
||||
m_checkbox_discord_presence = new ToolTipCheckBox(tr("Show Current Game on Discord"));
|
||||
basic_group_layout->addWidget(m_checkbox_discord_presence);
|
||||
#endif
|
||||
|
||||
|
@ -153,7 +154,7 @@ void GeneralPane::CreateBasic()
|
|||
speed_limit_layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||
basic_group_layout->addLayout(speed_limit_layout);
|
||||
|
||||
m_combobox_speedlimit = new QComboBox();
|
||||
m_combobox_speedlimit = new ToolTipComboBox();
|
||||
|
||||
m_combobox_speedlimit->addItem(tr("Unlimited"));
|
||||
for (int i = 10; i <= 200; i += 10) // from 10% to 200%
|
||||
|
@ -182,17 +183,11 @@ void GeneralPane::CreateFallbackRegion()
|
|||
fallback_region_dropdown_layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||
fallback_region_group_layout->addLayout(fallback_region_dropdown_layout);
|
||||
|
||||
m_combobox_fallback_region = new QComboBox(this);
|
||||
m_combobox_fallback_region = new ToolTipComboBox();
|
||||
fallback_region_dropdown_layout->addRow(tr("Fallback Region:"), m_combobox_fallback_region);
|
||||
|
||||
for (const QString& option : {tr("NTSC-J"), tr("NTSC-U"), tr("PAL"), tr("NTSC-K")})
|
||||
m_combobox_fallback_region->addItem(option);
|
||||
|
||||
auto* fallback_region_description =
|
||||
new QLabel(tr("Dolphin will use this for titles whose region cannot be determined "
|
||||
"automatically."));
|
||||
fallback_region_description->setWordWrap(true);
|
||||
fallback_region_group_layout->addWidget(fallback_region_description);
|
||||
}
|
||||
|
||||
#if defined(USE_ANALYTICS) && USE_ANALYTICS
|
||||
|
@ -203,9 +198,8 @@ void GeneralPane::CreateAnalytics()
|
|||
analytics_group->setLayout(analytics_group_layout);
|
||||
m_main_layout->addWidget(analytics_group);
|
||||
|
||||
m_checkbox_enable_analytics = new QCheckBox(tr("Enable Usage Statistics Reporting"));
|
||||
m_button_generate_new_identity =
|
||||
new NonDefaultQPushButton(tr("Generate a New Statistics Identity"));
|
||||
m_checkbox_enable_analytics = new ToolTipCheckBox(tr("Enable Usage Statistics Reporting"));
|
||||
m_button_generate_new_identity = new ToolTipPushButton(tr("Generate a New Statistics Identity"));
|
||||
analytics_group_layout->addWidget(m_checkbox_enable_analytics);
|
||||
analytics_group_layout->addWidget(m_button_generate_new_identity);
|
||||
}
|
||||
|
@ -340,12 +334,6 @@ void GeneralPane::OnSaveConfig()
|
|||
Settings::Instance().SetAnalyticsEnabled(m_checkbox_enable_analytics->isChecked());
|
||||
DolphinAnalytics::Instance().ReloadConfig();
|
||||
#endif
|
||||
Config::SetBaseOrCurrent(Config::MAIN_CPU_THREAD, m_checkbox_dualcore->isChecked());
|
||||
Settings::Instance().SetCheatsEnabled(m_checkbox_cheats->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::MAIN_OVERRIDE_REGION_SETTINGS,
|
||||
m_checkbox_override_region_settings->isChecked());
|
||||
Config::SetBase(Config::MAIN_AUTO_DISC_CHANGE, m_checkbox_auto_disc_change->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::MAIN_ENABLE_CHEATS, m_checkbox_cheats->isChecked());
|
||||
Settings::Instance().SetFallbackRegion(
|
||||
UpdateFallbackRegionFromIndex(m_combobox_fallback_region->currentIndex()));
|
||||
|
||||
|
@ -371,4 +359,107 @@ void GeneralPane::OnCodeHandlerChanged(int index)
|
|||
int code_handler_value = m_combobox_codehandler->itemData(index).toInt();
|
||||
Config::SetBaseOrCurrent(Config::MAIN_CODE_HANDLER, code_handler_value);
|
||||
Config::Save();
|
||||
void GeneralPane::AddDescriptions()
|
||||
{
|
||||
static constexpr char TR_DUALCORE_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Separates CPU and GPU emulation work to separate threads. Reduces single-thread "
|
||||
"burden by spreading Dolphin's heaviest load across two cores, which usually "
|
||||
"improves performance. However, it can result in glitches and crashes."
|
||||
"<br><br>This setting cannot be changed while emulation is active."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
|
||||
static constexpr char TR_CHEATS_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"Enables the use of AR and Gecko cheat codes which can be used to modify games' behavior. "
|
||||
"These codes can be configured with the Cheats Manager in the Tools menu."
|
||||
"<br><br>This setting cannot be changed while emulation is active."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
|
||||
static constexpr char TR_OVERRIDE_REGION_SETTINGS_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Lets you use languages and other region-related settings that the game may not "
|
||||
"be designed for. May cause various crashes and bugs."
|
||||
"<br><br>This setting cannot be changed while emulation is active."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
|
||||
static constexpr char TR_AUTO_DISC_CHANGE_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"Automatically changes the game disc when requested by games with two discs. This feature "
|
||||
"requires the game to be launched in one of the following ways:"
|
||||
"<br>- From the game list, with both discs being present in the game list."
|
||||
"<br>- With File > Open or the command line interface, with the paths to both discs being "
|
||||
"provided."
|
||||
"<br>- By launching an M3U file with File > Open or the command line interface."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
static constexpr char TR_DISCORD_PRESENCE_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows which game is active and the duration of your current play session in your "
|
||||
"Discord status."
|
||||
"<br><br>This setting cannot be changed while emulation is active."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
|
||||
#endif
|
||||
static constexpr char TR_SPEEDLIMIT_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Controls how fast emulation runs relative to the original hardware."
|
||||
"<br><br>Values higher than 100% will emulate faster than the original hardware "
|
||||
"can run, if your hardware is able to keep up. Values lower than 100% will slow "
|
||||
"emulation instead. Unlimited will emulate as fast as your hardware is able to."
|
||||
"<br><br><dolphin_emphasis>If unsure, select 100%.</dolphin_emphasis>");
|
||||
static constexpr char TR_UPDATE_TRACK_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"Selects which update track Dolphin uses when checking for updates at startup. If a new "
|
||||
"update is available, Dolphin will show a list of changes made since your current version "
|
||||
"and ask you if you want to update."
|
||||
"<br><br>The Dev track has the latest version of Dolphin which often updates multiple times "
|
||||
"per day. Select this track if you want the newest features and fixes."
|
||||
"<br><br>The Releases track has an update every few months. Some reasons you might prefer to "
|
||||
"use this track:"
|
||||
"<br>- You prefer using versions that have had additional testing."
|
||||
"<br>- NetPlay requires players to have the same Dolphin version, and the latest Release "
|
||||
"version will have the most players to match with."
|
||||
"<br>- You frequently use Dolphin's savestate system, which doesn't guarantee backward "
|
||||
"compatibility of savestates between Dolphin versions. If this applies to you, make sure you "
|
||||
"make an in-game save before updating (i.e. save your game in the same way you would on a "
|
||||
"physical GameCube or Wii), then load the in-game save after updating Dolphin and before "
|
||||
"making any new savestates."
|
||||
"<br><br>Selecting \"Don't Update\" will prevent Dolphin from automatically checking for "
|
||||
"updates."
|
||||
"<br><br><dolphin_emphasis>If unsure, select Releases.</dolphin_emphasis>");
|
||||
static constexpr char TR_FALLBACK_REGION_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Sets the region used for titles whose region cannot be determined automatically."
|
||||
"<br><br>This setting cannot be changed while emulation is active.");
|
||||
#if defined(USE_ANALYTICS) && USE_ANALYTICS
|
||||
static constexpr char TR_ENABLE_ANALYTICS_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"If selected, Dolphin can collect data on its performance, feature usage, emulated games, "
|
||||
"and configuration, as well as data on your system's hardware and operating system."
|
||||
"<br><br>No private data is ever collected. This data helps us understand how people and "
|
||||
"emulated games use Dolphin and prioritize our efforts. It also helps us identify rare "
|
||||
"configurations that are causing bugs, performance and stability issues.");
|
||||
static constexpr char TR_GENERATE_NEW_IDENTITY_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Generate a new anonymous ID for your usage statistics. This will cause any "
|
||||
"future statistics to be unassociated with your previous statistics.");
|
||||
#endif
|
||||
|
||||
m_checkbox_dualcore->SetDescription(tr(TR_DUALCORE_DESCRIPTION));
|
||||
|
||||
m_checkbox_cheats->SetDescription(tr(TR_CHEATS_DESCRIPTION));
|
||||
|
||||
m_checkbox_override_region_settings->SetDescription(tr(TR_OVERRIDE_REGION_SETTINGS_DESCRIPTION));
|
||||
|
||||
m_checkbox_auto_disc_change->SetDescription(tr(TR_AUTO_DISC_CHANGE_DESCRIPTION));
|
||||
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
m_checkbox_discord_presence->SetDescription(tr(TR_DISCORD_PRESENCE_DESCRIPTION));
|
||||
#endif
|
||||
|
||||
m_combobox_speedlimit->SetTitle(tr("Speed Limit"));
|
||||
m_combobox_speedlimit->SetDescription(tr(TR_SPEEDLIMIT_DESCRIPTION));
|
||||
|
||||
if (AutoUpdateChecker::SystemSupportsAutoUpdates())
|
||||
{
|
||||
m_combobox_update_track->SetTitle(tr("Auto Update"));
|
||||
m_combobox_update_track->SetDescription(tr(TR_UPDATE_TRACK_DESCRIPTION));
|
||||
}
|
||||
|
||||
m_combobox_fallback_region->SetTitle(tr("Fallback Region"));
|
||||
m_combobox_fallback_region->SetDescription(tr(TR_FALLBACK_REGION_DESCRIPTION));
|
||||
|
||||
#if defined(USE_ANALYTICS) && USE_ANALYTICS
|
||||
m_checkbox_enable_analytics->SetDescription(tr(TR_ENABLE_ANALYTICS_DESCRIPTION));
|
||||
|
||||
m_button_generate_new_identity->SetTitle(tr("Generate a New Statistics Identity"));
|
||||
m_button_generate_new_identity->SetDescription(tr(TR_GENERATE_NEW_IDENTITY_DESCRIPTION));
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <QWidget>
|
||||
|
||||
class ConfigBool;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
|
@ -12,6 +13,9 @@ class QPushButton;
|
|||
class QRadioButton;
|
||||
class QSlider;
|
||||
class QVBoxLayout;
|
||||
class ToolTipCheckBox;
|
||||
class ToolTipComboBox;
|
||||
class ToolTipPushButton;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
|
@ -30,6 +34,8 @@ private:
|
|||
void CreateBasic();
|
||||
//void CreateAutoUpdate();
|
||||
void CreateFallbackRegion();
|
||||
void AddDescriptions();
|
||||
|
||||
void LoadConfig();
|
||||
void OnSaveConfig();
|
||||
void OnEmulationStateChanged(Core::State state);
|
||||
|
@ -38,25 +44,23 @@ private:
|
|||
|
||||
// Widgets
|
||||
QVBoxLayout* m_main_layout;
|
||||
QComboBox* m_combobox_speedlimit;
|
||||
QComboBox* m_combobox_update_track;
|
||||
QComboBox* m_combobox_fallback_region;
|
||||
QCheckBox* m_checkbox_dualcore;
|
||||
QCheckBox* m_checkbox_cheats;
|
||||
QCheckBox* m_checkbox_override_region_settings;
|
||||
QCheckBox* m_checkbox_auto_disc_change;
|
||||
QComboBox* m_combobox_codehandler;
|
||||
ToolTipComboBox* m_combobox_speedlimit;
|
||||
ToolTipComboBox* m_combobox_update_track;
|
||||
ToolTipComboBox* m_combobox_fallback_region;
|
||||
ConfigBool* m_checkbox_dualcore;
|
||||
ConfigBool* m_checkbox_cheats;
|
||||
ConfigBool* m_checkbox_override_region_settings;
|
||||
ConfigBool* m_checkbox_auto_disc_change;
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
QCheckBox* m_checkbox_discord_presence;
|
||||
ToolTipCheckBox* m_checkbox_discord_presence;
|
||||
#endif
|
||||
QLabel* m_label_speedlimit;
|
||||
|
||||
// Analytics related
|
||||
#if defined(USE_ANALYTICS) && USE_ANALYTICS
|
||||
void CreateAnalytics();
|
||||
void GenerateNewIdentity();
|
||||
|
||||
QPushButton* m_button_generate_new_identity;
|
||||
QCheckBox* m_checkbox_enable_analytics;
|
||||
ToolTipPushButton* m_button_generate_new_identity;
|
||||
ToolTipCheckBox* m_checkbox_enable_analytics;
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -24,55 +24,60 @@
|
|||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/UISettings.h"
|
||||
|
||||
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
|
||||
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
|
||||
#include "DolphinQt/Config/ConfigControls/ConfigRadio.h"
|
||||
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
|
||||
#include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h"
|
||||
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
||||
#include "DolphinQt/QtUtils/SignalBlocking.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
static QComboBox* MakeLanguageComboBox()
|
||||
static ConfigStringChoice* MakeLanguageComboBox()
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
const QString name;
|
||||
const char* id;
|
||||
} languages[] = {
|
||||
{QStringLiteral(u"Bahasa Melayu"), "ms"}, // Malay
|
||||
{QStringLiteral(u"Catal\u00E0"), "ca"}, // Catalan
|
||||
{QStringLiteral(u"\u010Ce\u0161tina"), "cs"}, // Czech
|
||||
{QStringLiteral(u"Dansk"), "da"}, // Danish
|
||||
{QStringLiteral(u"Deutsch"), "de"}, // German
|
||||
{QStringLiteral(u"English"), "en"}, // English
|
||||
{QStringLiteral(u"Espa\u00F1ol"), "es"}, // Spanish
|
||||
{QStringLiteral(u"Fran\u00E7ais"), "fr"}, // French
|
||||
{QStringLiteral(u"Hrvatski"), "hr"}, // Croatian
|
||||
{QStringLiteral(u"Italiano"), "it"}, // Italian
|
||||
{QStringLiteral(u"Magyar"), "hu"}, // Hungarian
|
||||
{QStringLiteral(u"Nederlands"), "nl"}, // Dutch
|
||||
{QStringLiteral(u"Norsk bokm\u00E5l"), "nb"}, // Norwegian
|
||||
{QStringLiteral(u"Polski"), "pl"}, // Polish
|
||||
{QStringLiteral(u"Portugu\u00EAs"), "pt"}, // Portuguese
|
||||
{QStringLiteral(u"Portugu\u00EAs (Brasil)"), "pt_BR"}, // Portuguese (Brazil)
|
||||
{QStringLiteral(u"Rom\u00E2n\u0103"), "ro"}, // Romanian
|
||||
{QStringLiteral(u"Srpski"), "sr"}, // Serbian
|
||||
{QStringLiteral(u"Suomi"), "fi"}, // Finnish
|
||||
{QStringLiteral(u"Svenska"), "sv"}, // Swedish
|
||||
{QStringLiteral(u"T\u00FCrk\u00E7e"), "tr"}, // Turkish
|
||||
{QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"), "el"}, // Greek
|
||||
{QStringLiteral(u"\u0420\u0443\u0441\u0441\u043A\u0438\u0439"), "ru"}, // Russian
|
||||
{QStringLiteral(u"\u0627\u0644\u0639\u0631\u0628\u064A\u0629"), "ar"}, // Arabic
|
||||
{QStringLiteral(u"\u0641\u0627\u0631\u0633\u06CC"), "fa"}, // Farsi
|
||||
{QStringLiteral(u"\uD55C\uAD6D\uC5B4"), "ko"}, // Korean
|
||||
{QStringLiteral(u"\u65E5\u672C\u8A9E"), "ja"}, // Japanese
|
||||
{QStringLiteral(u"\u7B80\u4F53\u4E2D\u6587"), "zh_CN"}, // Simplified Chinese
|
||||
{QStringLiteral(u"\u7E41\u9AD4\u4E2D\u6587"), "zh_TW"}, // Traditional Chinese
|
||||
using QPair = std::pair<QString, QString>;
|
||||
std::vector<QPair> languages = {
|
||||
QPair{QObject::tr("<System Language>"), QString{}},
|
||||
QPair{QStringLiteral(u"Bahasa Melayu"), QStringLiteral("ms")}, // Malay
|
||||
QPair{QStringLiteral(u"Catal\u00E0"), QStringLiteral("ca")}, // Catalan
|
||||
QPair{QStringLiteral(u"\u010Ce\u0161tina"), QStringLiteral("cs")}, // Czech
|
||||
QPair{QStringLiteral(u"Dansk"), QStringLiteral("da")}, // Danish
|
||||
QPair{QStringLiteral(u"Deutsch"), QStringLiteral("de")}, // German
|
||||
QPair{QStringLiteral(u"English"), QStringLiteral("en")}, // English
|
||||
QPair{QStringLiteral(u"Espa\u00F1ol"), QStringLiteral("es")}, // Spanish
|
||||
QPair{QStringLiteral(u"Fran\u00E7ais"), QStringLiteral("fr")}, // French
|
||||
QPair{QStringLiteral(u"Hrvatski"), QStringLiteral("hr")}, // Croatian
|
||||
QPair{QStringLiteral(u"Italiano"), QStringLiteral("it")}, // Italian
|
||||
QPair{QStringLiteral(u"Magyar"), QStringLiteral("hu")}, // Hungarian
|
||||
QPair{QStringLiteral(u"Nederlands"), QStringLiteral("nl")}, // Dutch
|
||||
QPair{QStringLiteral(u"Norsk bokm\u00E5l"), QStringLiteral("nb")}, // Norwegian
|
||||
QPair{QStringLiteral(u"Polski"), QStringLiteral("pl")}, // Polish
|
||||
QPair{QStringLiteral(u"Portugu\u00EAs"), QStringLiteral("pt")}, // Portuguese
|
||||
QPair{QStringLiteral(u"Portugu\u00EAs (Brasil)"),
|
||||
QStringLiteral("pt_BR")}, // Portuguese (Brazil)
|
||||
QPair{QStringLiteral(u"Rom\u00E2n\u0103"), QStringLiteral("ro")}, // Romanian
|
||||
QPair{QStringLiteral(u"Srpski"), QStringLiteral("sr")}, // Serbian
|
||||
QPair{QStringLiteral(u"Suomi"), QStringLiteral("fi")}, // Finnish
|
||||
QPair{QStringLiteral(u"Svenska"), QStringLiteral("sv")}, // Swedish
|
||||
QPair{QStringLiteral(u"T\u00FCrk\u00E7e"), QStringLiteral("tr")}, // Turkish
|
||||
QPair{QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"),
|
||||
QStringLiteral("el")}, // Greek
|
||||
QPair{QStringLiteral(u"\u0420\u0443\u0441\u0441\u043A\u0438\u0439"),
|
||||
QStringLiteral("ru")}, // Russian
|
||||
QPair{QStringLiteral(u"\u0627\u0644\u0639\u0631\u0628\u064A\u0629"),
|
||||
QStringLiteral("ar")}, // Arabic
|
||||
QPair{QStringLiteral(u"\u0641\u0627\u0631\u0633\u06CC"), QStringLiteral("fa")}, // Farsi
|
||||
QPair{QStringLiteral(u"\uD55C\uAD6D\uC5B4"), QStringLiteral("ko")}, // Korean
|
||||
QPair{QStringLiteral(u"\u65E5\u672C\u8A9E"), QStringLiteral("ja")}, // Japanese
|
||||
QPair{QStringLiteral(u"\u7B80\u4F53\u4E2D\u6587"),
|
||||
QStringLiteral("zh_CN")}, // Simplified Chinese
|
||||
QPair{QStringLiteral(u"\u7E41\u9AD4\u4E2D\u6587"),
|
||||
QStringLiteral("zh_TW")}, // Traditional Chinese
|
||||
};
|
||||
|
||||
auto* combobox = new QComboBox();
|
||||
combobox->addItem(QObject::tr("<System Language>"), QString{});
|
||||
for (const auto& lang : languages)
|
||||
combobox->addItem(lang.name, QString::fromLatin1(lang.id));
|
||||
auto* const combobox = new ConfigStringChoice(languages, Config::MAIN_INTERFACE_LANGUAGE);
|
||||
|
||||
// The default, QComboBox::AdjustToContentsOnFirstShow, causes a noticeable pause when opening the
|
||||
// SettingWindow for the first time. The culprit seems to be non-Latin graphemes in the above
|
||||
|
@ -85,11 +90,12 @@ static QComboBox* MakeLanguageComboBox()
|
|||
InterfacePane::InterfacePane(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
CreateLayout();
|
||||
LoadConfig();
|
||||
UpdateShowDebuggingCheckbox();
|
||||
LoadUserStyle();
|
||||
ConnectLayout();
|
||||
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
||||
&InterfacePane::LoadConfig);
|
||||
&InterfacePane::UpdateShowDebuggingCheckbox);
|
||||
}
|
||||
|
||||
void InterfacePane::CreateLayout()
|
||||
|
@ -98,6 +104,7 @@ void InterfacePane::CreateLayout()
|
|||
// Create layout here
|
||||
CreateUI();
|
||||
CreateInGame();
|
||||
AddDescriptions();
|
||||
|
||||
m_main_layout->addStretch(1);
|
||||
setLayout(m_main_layout);
|
||||
|
@ -119,20 +126,20 @@ void InterfacePane::CreateUI()
|
|||
m_combobox_language = MakeLanguageComboBox();
|
||||
combobox_layout->addRow(tr("&Language:"), m_combobox_language);
|
||||
|
||||
// List avalable themes
|
||||
auto theme_paths =
|
||||
Common::DoFileSearch({File::GetUserPath(D_THEMES_IDX), File::GetSysDirectory() + THEMES_DIR});
|
||||
std::vector<std::string> theme_names;
|
||||
theme_names.reserve(theme_paths.size());
|
||||
std::transform(theme_paths.cbegin(), theme_paths.cend(), std::back_inserter(theme_names),
|
||||
PathToFileName);
|
||||
|
||||
// Theme Combobox
|
||||
m_combobox_theme = new QComboBox;
|
||||
m_combobox_theme = new ConfigStringChoice(theme_names, Config::MAIN_THEME_NAME);
|
||||
combobox_layout->addRow(tr("&Theme:"), m_combobox_theme);
|
||||
|
||||
// List avalable themes
|
||||
auto theme_search_results =
|
||||
Common::DoFileSearch({File::GetUserPath(D_THEMES_IDX), File::GetSysDirectory() + THEMES_DIR});
|
||||
for (const std::string& path : theme_search_results)
|
||||
{
|
||||
const QString qt_name = QString::fromStdString(PathToFileName(path));
|
||||
m_combobox_theme->addItem(qt_name);
|
||||
}
|
||||
// User Style Combobox
|
||||
m_combobox_userstyle = new QComboBox;
|
||||
m_combobox_userstyle = new ToolTipComboBox;
|
||||
m_label_userstyle = new QLabel(tr("Style:"));
|
||||
combobox_layout->addRow(m_label_userstyle, m_combobox_userstyle);
|
||||
auto userstyle_search_results = Common::DoFileSearch({File::GetUserPath(D_STYLES_IDX)});
|
||||
|
@ -154,12 +161,16 @@ void InterfacePane::CreateUI()
|
|||
}
|
||||
|
||||
// Checkboxes
|
||||
m_checkbox_use_builtin_title_database = new QCheckBox(tr("Use Built-In Database of Game Names"));
|
||||
m_checkbox_use_builtin_title_database = new ConfigBool(tr("Use Built-In Database of Game Names"),
|
||||
Config::MAIN_USE_BUILT_IN_TITLE_DATABASE);
|
||||
m_checkbox_use_covers =
|
||||
new QCheckBox(tr("Download Game Covers from GameTDB.com for Use in Grid Mode"));
|
||||
new ConfigBool(tr("Download Game Covers from GameTDB.com for Use in Grid Mode"),
|
||||
Config::MAIN_USE_GAME_COVERS);
|
||||
m_checkbox_show_debugging_ui = new ToolTipCheckBox(tr("Enable Debugging UI"));
|
||||
m_checkbox_focused_hotkeys = new QCheckBox(tr("Hotkeys Require Window Focus"));
|
||||
m_checkbox_disable_screensaver = new QCheckBox(tr("Inhibit Screensaver During Emulation"));
|
||||
m_checkbox_focused_hotkeys =
|
||||
new ConfigBool(tr("Hotkeys Require Window Focus"), Config::MAIN_FOCUSED_HOTKEYS);
|
||||
m_checkbox_disable_screensaver =
|
||||
new ConfigBool(tr("Inhibit Screensaver During Emulation"), Config::MAIN_DISABLE_SCREENSAVER);
|
||||
|
||||
groupbox_layout->addWidget(m_checkbox_use_builtin_title_database);
|
||||
groupbox_layout->addWidget(m_checkbox_use_covers);
|
||||
|
@ -175,34 +186,36 @@ void InterfacePane::CreateInGame()
|
|||
groupbox->setLayout(groupbox_layout);
|
||||
m_main_layout->addWidget(groupbox);
|
||||
|
||||
m_checkbox_top_window = new QCheckBox(tr("Keep Window on Top"));
|
||||
m_checkbox_confirm_on_stop = new QCheckBox(tr("Confirm on Stop"));
|
||||
m_checkbox_use_panic_handlers = new QCheckBox(tr("Use Panic Handlers"));
|
||||
m_checkbox_enable_osd = new QCheckBox(tr("Show On-Screen Display Messages"));
|
||||
m_checkbox_show_active_title = new QCheckBox(tr("Show Active Title in Window Title"));
|
||||
m_checkbox_pause_on_focus_lost = new QCheckBox(tr("Pause on Focus Loss"));
|
||||
m_checkbox_top_window = new ConfigBool(tr("Keep Window on Top"), Config::MAIN_KEEP_WINDOW_ON_TOP);
|
||||
m_checkbox_confirm_on_stop = new ConfigBool(tr("Confirm on Stop"), Config::MAIN_CONFIRM_ON_STOP);
|
||||
m_checkbox_use_panic_handlers =
|
||||
new ConfigBool(tr("Use Panic Handlers"), Config::MAIN_USE_PANIC_HANDLERS);
|
||||
m_checkbox_enable_osd =
|
||||
new ConfigBool(tr("Show On-Screen Display Messages"), Config::MAIN_OSD_MESSAGES);
|
||||
m_checkbox_show_active_title =
|
||||
new ConfigBool(tr("Show Active Title in Window Title"), Config::MAIN_SHOW_ACTIVE_TITLE);
|
||||
m_checkbox_pause_on_focus_lost =
|
||||
new ConfigBool(tr("Pause on Focus Loss"), Config::MAIN_PAUSE_ON_FOCUS_LOST);
|
||||
|
||||
auto* mouse_groupbox = new QGroupBox(tr("Mouse Cursor Visibility"));
|
||||
auto* m_vboxlayout_hide_mouse = new QVBoxLayout;
|
||||
mouse_groupbox->setLayout(m_vboxlayout_hide_mouse);
|
||||
|
||||
m_radio_cursor_visible_movement = new QRadioButton(tr("On Movement"));
|
||||
m_radio_cursor_visible_movement->setToolTip(
|
||||
tr("Mouse Cursor hides after inactivity and returns upon Mouse Cursor movement."));
|
||||
m_radio_cursor_visible_never = new QRadioButton(tr("Never"));
|
||||
m_radio_cursor_visible_never->setToolTip(
|
||||
tr("Mouse Cursor will never be visible while a game is running."));
|
||||
m_radio_cursor_visible_always = new QRadioButton(tr("Always"));
|
||||
m_radio_cursor_visible_always->setToolTip(tr("Mouse Cursor will always be visible."));
|
||||
m_radio_cursor_visible_movement =
|
||||
new ConfigRadioInt(tr("On Movement"), Config::MAIN_SHOW_CURSOR,
|
||||
static_cast<int>(Config::ShowCursor::OnMovement));
|
||||
m_radio_cursor_visible_never = new ConfigRadioInt(tr("Never"), Config::MAIN_SHOW_CURSOR,
|
||||
static_cast<int>(Config::ShowCursor::Never));
|
||||
m_radio_cursor_visible_always = new ConfigRadioInt(
|
||||
tr("Always"), Config::MAIN_SHOW_CURSOR, static_cast<int>(Config::ShowCursor::Constantly));
|
||||
|
||||
m_vboxlayout_hide_mouse->addWidget(m_radio_cursor_visible_movement);
|
||||
m_vboxlayout_hide_mouse->addWidget(m_radio_cursor_visible_never);
|
||||
m_vboxlayout_hide_mouse->addWidget(m_radio_cursor_visible_always);
|
||||
|
||||
m_checkbox_lock_mouse = new ConfigBool(tr("Lock Mouse Cursor"), Config::MAIN_LOCK_CURSOR);
|
||||
// this ends up not being managed unless _WIN32, so lets not leak
|
||||
m_checkbox_lock_mouse = new QCheckBox(tr("Lock Mouse Cursor"), this);
|
||||
m_checkbox_lock_mouse->setToolTip(tr("Will lock the Mouse Cursor to the Render Widget as long as "
|
||||
"it has focus. You can set a hotkey to unlock it."));
|
||||
m_checkbox_lock_mouse->setParent(this);
|
||||
|
||||
mouse_groupbox->setLayout(m_vboxlayout_hide_mouse);
|
||||
groupbox_layout->addWidget(m_checkbox_top_window);
|
||||
|
@ -221,62 +234,58 @@ void InterfacePane::CreateInGame()
|
|||
|
||||
void InterfacePane::ConnectLayout()
|
||||
{
|
||||
connect(m_checkbox_use_builtin_title_database, &QCheckBox::toggled, this,
|
||||
&InterfacePane::OnSaveConfig);
|
||||
connect(m_checkbox_use_covers, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||
connect(m_checkbox_disable_screensaver, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||
connect(m_checkbox_show_debugging_ui, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||
connect(m_checkbox_focused_hotkeys, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||
connect(m_combobox_theme, &QComboBox::currentIndexChanged, this, [this](int index) {
|
||||
Settings::Instance().SetThemeName(m_combobox_theme->itemText(index));
|
||||
});
|
||||
connect(m_checkbox_use_builtin_title_database, &QCheckBox::toggled, &Settings::Instance(),
|
||||
&Settings::GameListRefreshRequested);
|
||||
connect(m_checkbox_use_covers, &QCheckBox::toggled, &Settings::Instance(),
|
||||
&Settings::MetadataRefreshRequested);
|
||||
connect(m_checkbox_show_debugging_ui, &QCheckBox::toggled, &Settings::Instance(),
|
||||
&Settings::SetDebugModeEnabled);
|
||||
connect(m_combobox_theme, &QComboBox::currentIndexChanged, &Settings::Instance(),
|
||||
&Settings::ThemeChanged);
|
||||
connect(m_combobox_userstyle, &QComboBox::currentIndexChanged, this,
|
||||
&InterfacePane::OnSaveConfig);
|
||||
connect(m_combobox_language, &QComboBox::currentIndexChanged, this, &InterfacePane::OnSaveConfig);
|
||||
connect(m_checkbox_top_window, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||
connect(m_checkbox_confirm_on_stop, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||
connect(m_checkbox_use_panic_handlers, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||
connect(m_checkbox_show_active_title, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||
connect(m_checkbox_enable_osd, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||
connect(m_checkbox_pause_on_focus_lost, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||
connect(m_radio_cursor_visible_movement, &QRadioButton::toggled, this,
|
||||
&InterfacePane::OnCursorVisibleMovement);
|
||||
connect(m_radio_cursor_visible_never, &QRadioButton::toggled, this,
|
||||
&InterfacePane::OnCursorVisibleNever);
|
||||
connect(m_radio_cursor_visible_always, &QRadioButton::toggled, this,
|
||||
&InterfacePane::OnCursorVisibleAlways);
|
||||
&InterfacePane::OnUserStyleChanged);
|
||||
connect(m_combobox_language, &QComboBox::currentIndexChanged, this,
|
||||
&InterfacePane::OnLanguageChanged);
|
||||
connect(m_checkbox_top_window, &QCheckBox::toggled, &Settings::Instance(),
|
||||
&Settings::KeepWindowOnTopChanged);
|
||||
connect(m_radio_cursor_visible_movement, &ConfigRadioInt::OnSelected, &Settings::Instance(),
|
||||
&Settings::CursorVisibilityChanged);
|
||||
connect(m_radio_cursor_visible_never, &ConfigRadioInt::OnSelected, &Settings::Instance(),
|
||||
&Settings::CursorVisibilityChanged);
|
||||
connect(m_radio_cursor_visible_always, &ConfigRadioInt::OnSelected, &Settings::Instance(),
|
||||
&Settings::CursorVisibilityChanged);
|
||||
connect(m_checkbox_lock_mouse, &QCheckBox::toggled, &Settings::Instance(),
|
||||
&Settings::SetLockCursor);
|
||||
&Settings::LockCursorChanged);
|
||||
}
|
||||
|
||||
void InterfacePane::LoadConfig()
|
||||
void InterfacePane::UpdateShowDebuggingCheckbox()
|
||||
{
|
||||
SignalBlocking(m_checkbox_use_builtin_title_database)
|
||||
->setChecked(Config::Get(Config::MAIN_USE_BUILT_IN_TITLE_DATABASE));
|
||||
SignalBlocking(m_checkbox_show_debugging_ui)
|
||||
->setChecked(Settings::Instance().IsDebugModeEnabled());
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
static constexpr char TR_SHOW_DEBUGGING_UI_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"Shows Dolphin's debugging user interface. This lets you view and modify a game's code and "
|
||||
"memory contents, set debugging breakpoints, examine network requests, and more."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
|
||||
static constexpr char TR_DISABLED_IN_HARDCORE_DESCRIPTION[] =
|
||||
QT_TR_NOOP("<dolphin_emphasis>Disabled in Hardcore Mode.</dolphin_emphasis>");
|
||||
|
||||
bool hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
SignalBlocking(m_checkbox_show_debugging_ui)->setEnabled(!hardcore);
|
||||
if (hardcore)
|
||||
{
|
||||
m_checkbox_show_debugging_ui->SetDescription(
|
||||
tr("<dolphin_emphasis>Disabled in Hardcore Mode.</dolphin_emphasis>"));
|
||||
m_checkbox_show_debugging_ui->SetDescription(tr("%1<br><br>%2")
|
||||
.arg(tr(TR_SHOW_DEBUGGING_UI_DESCRIPTION))
|
||||
.arg(tr(TR_DISABLED_IN_HARDCORE_DESCRIPTION)));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_checkbox_show_debugging_ui->SetDescription({});
|
||||
m_checkbox_show_debugging_ui->SetDescription(tr(TR_SHOW_DEBUGGING_UI_DESCRIPTION));
|
||||
}
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
SignalBlocking(m_combobox_language)
|
||||
->setCurrentIndex(m_combobox_language->findData(
|
||||
QString::fromStdString(Config::Get(Config::MAIN_INTERFACE_LANGUAGE))));
|
||||
SignalBlocking(m_combobox_theme)
|
||||
->setCurrentIndex(
|
||||
m_combobox_theme->findText(QString::fromStdString(Config::Get(Config::MAIN_THEME_NAME))));
|
||||
}
|
||||
|
||||
void InterfacePane::LoadUserStyle()
|
||||
{
|
||||
const Settings::StyleType style_type = Settings::Instance().GetStyleType();
|
||||
const QString userstyle = Settings::Instance().GetUserStyleName();
|
||||
const int index = style_type == Settings::StyleType::User ?
|
||||
|
@ -285,37 +294,10 @@ void InterfacePane::LoadConfig()
|
|||
|
||||
if (index > 0)
|
||||
SignalBlocking(m_combobox_userstyle)->setCurrentIndex(index);
|
||||
|
||||
// Render Window Options
|
||||
SignalBlocking(m_checkbox_top_window)
|
||||
->setChecked(Settings::Instance().IsKeepWindowOnTopEnabled());
|
||||
SignalBlocking(m_checkbox_confirm_on_stop)->setChecked(Config::Get(Config::MAIN_CONFIRM_ON_STOP));
|
||||
SignalBlocking(m_checkbox_use_panic_handlers)
|
||||
->setChecked(Config::Get(Config::MAIN_USE_PANIC_HANDLERS));
|
||||
SignalBlocking(m_checkbox_enable_osd)->setChecked(Config::Get(Config::MAIN_OSD_MESSAGES));
|
||||
SignalBlocking(m_checkbox_show_active_title)
|
||||
->setChecked(Config::Get(Config::MAIN_SHOW_ACTIVE_TITLE));
|
||||
SignalBlocking(m_checkbox_pause_on_focus_lost)
|
||||
->setChecked(Config::Get(Config::MAIN_PAUSE_ON_FOCUS_LOST));
|
||||
SignalBlocking(m_checkbox_use_covers)->setChecked(Config::Get(Config::MAIN_USE_GAME_COVERS));
|
||||
SignalBlocking(m_checkbox_focused_hotkeys)->setChecked(Config::Get(Config::MAIN_FOCUSED_HOTKEYS));
|
||||
SignalBlocking(m_radio_cursor_visible_movement)
|
||||
->setChecked(Settings::Instance().GetCursorVisibility() == Config::ShowCursor::OnMovement);
|
||||
SignalBlocking(m_radio_cursor_visible_always)
|
||||
->setChecked(Settings::Instance().GetCursorVisibility() == Config::ShowCursor::Constantly);
|
||||
SignalBlocking(m_radio_cursor_visible_never)
|
||||
->setChecked(Settings::Instance().GetCursorVisibility() == Config::ShowCursor::Never);
|
||||
|
||||
SignalBlocking(m_checkbox_lock_mouse)->setChecked(Settings::Instance().GetLockCursor());
|
||||
SignalBlocking(m_checkbox_disable_screensaver)
|
||||
->setChecked(Config::Get(Config::MAIN_DISABLE_SCREENSAVER));
|
||||
}
|
||||
|
||||
void InterfacePane::OnSaveConfig()
|
||||
void InterfacePane::OnUserStyleChanged()
|
||||
{
|
||||
Config::SetBase(Config::MAIN_USE_BUILT_IN_TITLE_DATABASE,
|
||||
m_checkbox_use_builtin_title_database->isChecked());
|
||||
Settings::Instance().SetDebugModeEnabled(m_checkbox_show_debugging_ui->isChecked());
|
||||
const auto selected_style = m_combobox_userstyle->currentData();
|
||||
bool is_builtin_type = false;
|
||||
const int style_type_int = selected_style.toInt(&is_builtin_type);
|
||||
|
@ -325,49 +307,114 @@ void InterfacePane::OnSaveConfig()
|
|||
if (!is_builtin_type)
|
||||
Settings::Instance().SetUserStyleName(selected_style.toString());
|
||||
Settings::Instance().ApplyStyle();
|
||||
|
||||
// Render Window Options
|
||||
Settings::Instance().SetKeepWindowOnTop(m_checkbox_top_window->isChecked());
|
||||
Config::SetBase(Config::MAIN_CONFIRM_ON_STOP, m_checkbox_confirm_on_stop->isChecked());
|
||||
Config::SetBase(Config::MAIN_USE_PANIC_HANDLERS, m_checkbox_use_panic_handlers->isChecked());
|
||||
Config::SetBase(Config::MAIN_OSD_MESSAGES, m_checkbox_enable_osd->isChecked());
|
||||
Config::SetBase(Config::MAIN_SHOW_ACTIVE_TITLE, m_checkbox_show_active_title->isChecked());
|
||||
Config::SetBase(Config::MAIN_PAUSE_ON_FOCUS_LOST, m_checkbox_pause_on_focus_lost->isChecked());
|
||||
|
||||
auto new_language = m_combobox_language->currentData().toString().toStdString();
|
||||
if (new_language != Config::Get(Config::MAIN_INTERFACE_LANGUAGE))
|
||||
{
|
||||
Config::SetBase(Config::MAIN_INTERFACE_LANGUAGE, new_language);
|
||||
ModalMessageBox::information(
|
||||
this, tr("Restart Required"),
|
||||
tr("You must restart Dolphin in order for the change to take effect."));
|
||||
}
|
||||
|
||||
const bool use_covers = m_checkbox_use_covers->isChecked();
|
||||
|
||||
if (use_covers != Config::Get(Config::MAIN_USE_GAME_COVERS))
|
||||
{
|
||||
Config::SetBase(Config::MAIN_USE_GAME_COVERS, use_covers);
|
||||
Settings::Instance().RefreshMetadata();
|
||||
}
|
||||
|
||||
Config::SetBase(Config::MAIN_FOCUSED_HOTKEYS, m_checkbox_focused_hotkeys->isChecked());
|
||||
Config::SetBase(Config::MAIN_DISABLE_SCREENSAVER, m_checkbox_disable_screensaver->isChecked());
|
||||
|
||||
Config::Save();
|
||||
}
|
||||
|
||||
void InterfacePane::OnCursorVisibleMovement()
|
||||
void InterfacePane::OnLanguageChanged()
|
||||
{
|
||||
Settings::Instance().SetCursorVisibility(Config::ShowCursor::OnMovement);
|
||||
ModalMessageBox::information(
|
||||
this, tr("Restart Required"),
|
||||
tr("You must restart Dolphin in order for the change to take effect."));
|
||||
}
|
||||
|
||||
void InterfacePane::OnCursorVisibleNever()
|
||||
void InterfacePane::AddDescriptions()
|
||||
{
|
||||
Settings::Instance().SetCursorVisibility(Config::ShowCursor::Never);
|
||||
}
|
||||
static constexpr char TR_TITLE_DATABASE_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"Uses Dolphin's database of properly formatted names in the game list's Title column."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
|
||||
static constexpr char TR_THEME_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Changes the appearance and color of Dolphin's buttons."
|
||||
"<br><br><dolphin_emphasis>If unsure, select Clean.</dolphin_emphasis>");
|
||||
static constexpr char TR_TOP_WINDOW_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Forces the render window to stay on top of other windows and applications."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
|
||||
static constexpr char TR_LANGUAGE_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"Sets the language displayed by Dolphin's user interface."
|
||||
"<br><br>Changes to this setting only take effect once Dolphin is restarted."
|
||||
"<br><br><dolphin_emphasis>If unsure, select <System Language>.</dolphin_emphasis>");
|
||||
static constexpr char TR_FOCUSED_HOTKEYS_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Requires the render window to be focused for hotkeys to take effect."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
|
||||
static constexpr char TR_USE_COVERS_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Downloads full game covers from GameTDB.com to display in the game list's Grid "
|
||||
"View. If this setting is unchecked, the game list displays a banner from the "
|
||||
"game's save data, and if the game has no save file, displays a generic "
|
||||
"banner instead."
|
||||
"<br><br>List View will always use the save file banners."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
|
||||
static constexpr char TR_DISABLE_SCREENSAVER_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Disables your screensaver while running a game."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
|
||||
static constexpr char TR_CONFIRM_ON_STOP_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Prompts you to confirm that you want to end emulation when you press Stop."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
|
||||
static constexpr char TR_USE_PANIC_HANDLERS_DESCRIPTION[] =
|
||||
QT_TR_NOOP("In the event of an error, Dolphin will halt to inform you of the error and "
|
||||
"present choices on how to proceed. With this option disabled, Dolphin will "
|
||||
"\"ignore\" all errors. Emulation will not be halted and you will not be notified."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
|
||||
static constexpr char TR_ENABLE_OSD_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows on-screen display messages over the render window. These messages "
|
||||
"disappear after several seconds."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
|
||||
static constexpr char TR_SHOW_ACTIVE_TITLE_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows the active game title in the render window's title bar."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
|
||||
static constexpr char TR_PAUSE_ON_FOCUS_LOST_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Pauses the game whenever the render window isn't focused."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
|
||||
static constexpr char TR_LOCK_MOUSE_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Locks the mouse cursor to the Render Widget as long as it has focus. You can "
|
||||
"set a hotkey to unlock it."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
|
||||
static constexpr char TR_CURSOR_VISIBLE_MOVEMENT_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows the mouse cursor briefly whenever it has recently moved, then hides it."
|
||||
"<br><br><dolphin_emphasis>If unsure, select this mode.</dolphin_emphasis>");
|
||||
static constexpr char TR_CURSOR_VISIBLE_NEVER_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"Hides the mouse cursor whenever it is inside the render window and the render window is "
|
||||
"focused."
|
||||
"<br><br><dolphin_emphasis>If unsure, select "On Movement".</dolphin_emphasis>");
|
||||
static constexpr char TR_CURSOR_VISIBLE_ALWAYS_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"Shows the mouse cursor at all times."
|
||||
"<br><br><dolphin_emphasis>If unsure, select "On Movement".</dolphin_emphasis>");
|
||||
static constexpr char TR_USER_STYLE_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Sets the style of Dolphin's user interface. Any custom styles that you have "
|
||||
"added will be presented here, allowing you to switch to them."
|
||||
"<br><br><dolphin_emphasis>If unsure, select (System).</dolphin_emphasis>");
|
||||
|
||||
void InterfacePane::OnCursorVisibleAlways()
|
||||
{
|
||||
Settings::Instance().SetCursorVisibility(Config::ShowCursor::Constantly);
|
||||
m_checkbox_use_builtin_title_database->SetDescription(tr(TR_TITLE_DATABASE_DESCRIPTION));
|
||||
|
||||
m_combobox_theme->SetTitle(tr("Theme"));
|
||||
m_combobox_theme->SetDescription(tr(TR_THEME_DESCRIPTION));
|
||||
|
||||
m_checkbox_top_window->SetDescription(tr(TR_TOP_WINDOW_DESCRIPTION));
|
||||
|
||||
m_combobox_language->SetTitle(tr("Language"));
|
||||
m_combobox_language->SetDescription(tr(TR_LANGUAGE_DESCRIPTION));
|
||||
|
||||
m_checkbox_focused_hotkeys->SetDescription(tr(TR_FOCUSED_HOTKEYS_DESCRIPTION));
|
||||
|
||||
m_checkbox_use_covers->SetDescription(tr(TR_USE_COVERS_DESCRIPTION));
|
||||
|
||||
m_checkbox_disable_screensaver->SetDescription(tr(TR_DISABLE_SCREENSAVER_DESCRIPTION));
|
||||
|
||||
m_checkbox_confirm_on_stop->SetDescription(tr(TR_CONFIRM_ON_STOP_DESCRIPTION));
|
||||
|
||||
m_checkbox_use_panic_handlers->SetDescription(tr(TR_USE_PANIC_HANDLERS_DESCRIPTION));
|
||||
|
||||
m_checkbox_enable_osd->SetDescription(tr(TR_ENABLE_OSD_DESCRIPTION));
|
||||
|
||||
m_checkbox_show_active_title->SetDescription(tr(TR_SHOW_ACTIVE_TITLE_DESCRIPTION));
|
||||
|
||||
m_checkbox_pause_on_focus_lost->SetDescription(tr(TR_PAUSE_ON_FOCUS_LOST_DESCRIPTION));
|
||||
|
||||
m_checkbox_lock_mouse->SetDescription(tr(TR_LOCK_MOUSE_DESCRIPTION));
|
||||
|
||||
m_radio_cursor_visible_movement->SetDescription(tr(TR_CURSOR_VISIBLE_MOVEMENT_DESCRIPTION));
|
||||
|
||||
m_radio_cursor_visible_never->SetDescription(tr(TR_CURSOR_VISIBLE_NEVER_DESCRIPTION));
|
||||
|
||||
m_radio_cursor_visible_always->SetDescription(tr(TR_CURSOR_VISIBLE_ALWAYS_DESCRIPTION));
|
||||
|
||||
m_combobox_userstyle->SetTitle(tr("Style"));
|
||||
m_combobox_userstyle->SetDescription(tr(TR_USER_STYLE_DESCRIPTION));
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@
|
|||
|
||||
#include <QWidget>
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class ConfigBool;
|
||||
class ConfigRadioInt;
|
||||
class ConfigStringChoice;
|
||||
class QLabel;
|
||||
class QRadioButton;
|
||||
class QVBoxLayout;
|
||||
class ToolTipCheckBox;
|
||||
class ToolTipComboBox;
|
||||
|
||||
class InterfacePane final : public QWidget
|
||||
{
|
||||
|
@ -22,34 +23,33 @@ private:
|
|||
void CreateLayout();
|
||||
void CreateUI();
|
||||
void CreateInGame();
|
||||
void AddDescriptions();
|
||||
void ConnectLayout();
|
||||
void LoadConfig();
|
||||
void OnSaveConfig();
|
||||
void OnCursorVisibleMovement();
|
||||
void OnCursorVisibleNever();
|
||||
void OnCursorVisibleAlways();
|
||||
void UpdateShowDebuggingCheckbox();
|
||||
void LoadUserStyle();
|
||||
void OnUserStyleChanged();
|
||||
void OnLanguageChanged();
|
||||
|
||||
QVBoxLayout* m_main_layout;
|
||||
QComboBox* m_combobox_language;
|
||||
ConfigStringChoice* m_combobox_language;
|
||||
|
||||
QComboBox* m_combobox_theme;
|
||||
QComboBox* m_combobox_userstyle;
|
||||
ConfigStringChoice* m_combobox_theme;
|
||||
ToolTipComboBox* m_combobox_userstyle;
|
||||
QLabel* m_label_userstyle;
|
||||
QCheckBox* m_checkbox_top_window;
|
||||
QCheckBox* m_checkbox_use_builtin_title_database;
|
||||
QCheckBox* m_checkbox_use_userstyle;
|
||||
ConfigBool* m_checkbox_top_window;
|
||||
ConfigBool* m_checkbox_use_builtin_title_database;
|
||||
ToolTipCheckBox* m_checkbox_show_debugging_ui;
|
||||
QCheckBox* m_checkbox_focused_hotkeys;
|
||||
QCheckBox* m_checkbox_use_covers;
|
||||
QCheckBox* m_checkbox_disable_screensaver;
|
||||
ConfigBool* m_checkbox_focused_hotkeys;
|
||||
ConfigBool* m_checkbox_use_covers;
|
||||
ConfigBool* m_checkbox_disable_screensaver;
|
||||
|
||||
QCheckBox* m_checkbox_confirm_on_stop;
|
||||
QCheckBox* m_checkbox_use_panic_handlers;
|
||||
QCheckBox* m_checkbox_enable_osd;
|
||||
QCheckBox* m_checkbox_show_active_title;
|
||||
QCheckBox* m_checkbox_pause_on_focus_lost;
|
||||
QRadioButton* m_radio_cursor_visible_movement;
|
||||
QRadioButton* m_radio_cursor_visible_never;
|
||||
QRadioButton* m_radio_cursor_visible_always;
|
||||
QCheckBox* m_checkbox_lock_mouse;
|
||||
ConfigBool* m_checkbox_confirm_on_stop;
|
||||
ConfigBool* m_checkbox_use_panic_handlers;
|
||||
ConfigBool* m_checkbox_enable_osd;
|
||||
ConfigBool* m_checkbox_show_active_title;
|
||||
ConfigBool* m_checkbox_pause_on_focus_lost;
|
||||
ConfigRadioInt* m_radio_cursor_visible_movement;
|
||||
ConfigRadioInt* m_radio_cursor_visible_never;
|
||||
ConfigRadioInt* m_radio_cursor_visible_always;
|
||||
ConfigBool* m_checkbox_lock_mouse;
|
||||
};
|
||||
|
|
|
@ -113,7 +113,7 @@ void USBDeviceAddToWhitelistDialog::RefreshDeviceList()
|
|||
auto whitelist = Config::GetUSBDeviceWhitelist();
|
||||
for (const auto& device : current_devices)
|
||||
{
|
||||
if (whitelist.count({device.first.first, device.first.second}) != 0)
|
||||
if (whitelist.contains({device.first.first, device.first.second}))
|
||||
continue;
|
||||
usb_inserted_devices_list->addItem(QString::fromStdString(device.second));
|
||||
}
|
||||
|
|
|
@ -503,7 +503,7 @@ void WiiPane::PopulateUSBPassthroughListWidget()
|
|||
void WiiPane::BrowseSDRaw()
|
||||
{
|
||||
QString file = QDir::toNativeSeparators(DolphinFileDialog::getOpenFileName(
|
||||
this, tr("Select a SD Card Image"),
|
||||
this, tr("Select SD Card Image"),
|
||||
QString::fromStdString(Config::Get(Config::MAIN_WII_SD_CARD_IMAGE_PATH)),
|
||||
tr("SD Card Image (*.raw);;"
|
||||
"All Files (*)")));
|
||||
|
@ -520,7 +520,7 @@ void WiiPane::SetSDRaw(const QString& path)
|
|||
void WiiPane::BrowseSDSyncFolder()
|
||||
{
|
||||
QString file = QDir::toNativeSeparators(DolphinFileDialog::getExistingDirectory(
|
||||
this, tr("Select a Folder to sync with the SD Card Image"),
|
||||
this, tr("Select a Folder to Sync with the SD Card Image"),
|
||||
QString::fromStdString(Config::Get(Config::MAIN_WII_SD_CARD_SYNC_FOLDER_PATH))));
|
||||
if (!file.isEmpty())
|
||||
SetSDSyncFolder(file);
|
||||
|
|
|
@ -81,7 +81,7 @@ SkylanderPortalWindow::SkylanderPortalWindow(QWidget* parent) : QWidget(parent)
|
|||
m_collection_path = QDir::toNativeSeparators(skylanders_folder.path()) + QDir::separator();
|
||||
m_last_skylander_path = m_collection_path;
|
||||
m_path_edit->setText(m_collection_path);
|
||||
};
|
||||
}
|
||||
|
||||
SkylanderPortalWindow::~SkylanderPortalWindow() = default;
|
||||
|
||||
|
|
|
@ -493,3 +493,10 @@ QTableCornerButton::section {
|
|||
border-left: 0px;
|
||||
border-bottom: 0px;
|
||||
}
|
||||
|
||||
QProgressBar {
|
||||
border: 2px solid grey;
|
||||
border-radius: 5px;
|
||||
background-color: #202020;
|
||||
}
|
||||
|
|
@ -6,7 +6,9 @@
|
|||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QEvent>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
@ -17,6 +19,7 @@
|
|||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#include "DolphinQt/Host.h"
|
||||
#include "DolphinQt/QtUtils/AspectRatioWidget.h"
|
||||
#include "DolphinQt/QtUtils/QueueOnObject.h"
|
||||
#include "DolphinQt/Resources.h"
|
||||
|
@ -268,3 +271,16 @@ std::optional<ControlState> TASInputWindow::GetSpinBox(TASSpinBox* spin, int zer
|
|||
|
||||
return (spin->GetValue() - zero) / scale;
|
||||
}
|
||||
|
||||
void TASInputWindow::changeEvent(QEvent* const event)
|
||||
{
|
||||
if (event->type() == QEvent::ActivationChange)
|
||||
{
|
||||
const bool active_window_is_tas_input =
|
||||
qobject_cast<TASInputWindow*>(QApplication::activeWindow()) != nullptr;
|
||||
|
||||
// Switching between TAS Input windows will call SetTASInputFocus(true) twice, but that's fine.
|
||||
Host::GetInstance()->SetTASInputFocus(active_window_is_tas_input);
|
||||
}
|
||||
QDialog::changeEvent(event);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
class QBoxLayout;
|
||||
class QCheckBox;
|
||||
class QDialog;
|
||||
class QEvent;
|
||||
class QGroupBox;
|
||||
class QSpinBox;
|
||||
class QString;
|
||||
|
@ -68,6 +69,8 @@ protected:
|
|||
QKeySequence shortcut_key_sequence, Qt::Orientation orientation,
|
||||
QWidget* shortcut_widget);
|
||||
|
||||
void changeEvent(QEvent* event) override;
|
||||
|
||||
QGroupBox* m_settings_box;
|
||||
QCheckBox* m_use_controller;
|
||||
QSpinBox* m_turbo_press_frames;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue