Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 186 additions & 62 deletions pcsx2-qt/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
#include "pcsx2/ImGui/FullscreenUI.h"
#include "pcsx2/MTGS.h"
#include "pcsx2/PerformanceMetrics.h"
#include "pcsx2/VMManager.h"
#include "pcsx2/ImGui/ImGuiOverlays.h"
#include "pcsx2/SPU2/spu2.h"
#include "pcsx2/Recording/InputRecording.h"
#include "pcsx2/Recording/InputRecordingControls.h"
#include "pcsx2/SaveState.h"
Expand All @@ -54,8 +57,11 @@
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QProgressBar>
#include <QtWidgets/QSlider>
#include <QtWidgets/QStyle>
#include <QtWidgets/QStyleFactory>
#include <QtWidgets/QWidgetAction>
#include <QtWidgets/QHBoxLayout>

#ifdef _WIN32
#include "common/RedtapeWindows.h"
Expand Down Expand Up @@ -185,6 +191,7 @@ void MainWindow::setupAdditionalUi()
{
makeIconsMasks(menuBar());
updateAdvancedSettingsVisibility();
setupStatusBarWidgets();

const bool toolbar_visible = Host::GetBaseBoolSettingValue("UI", "ShowToolbar", false);
m_ui.actionViewToolbar->setChecked(toolbar_visible);
Expand All @@ -195,58 +202,6 @@ void MainWindow::setupAdditionalUi()
m_ui.toolBar->setMovable(!toolbars_locked);
m_ui.toolBar->setContextMenuPolicy(Qt::PreventContextMenu);

const bool status_bar_visible = Host::GetBaseBoolSettingValue("UI", "ShowStatusBar", true);
m_ui.actionViewStatusBar->setChecked(status_bar_visible);
m_ui.statusBar->setVisible(status_bar_visible);

const bool show_game_grid = Host::GetBaseBoolSettingValue("UI", "GameListGridView", false);
updateGameGridActions(show_game_grid);

m_game_list_widget = new GameListWidget(getContentParent());
m_game_list_widget->initialize();
m_ui.actionGridViewShowTitles->setChecked(m_game_list_widget->getShowGridCoverTitles());
m_ui.mainContainer->addWidget(m_game_list_widget);

m_status_progress_widget = new QProgressBar(m_ui.statusBar);
m_status_progress_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_progress_widget->setFixedSize(140, 16);
m_status_progress_widget->setMinimum(0);
m_status_progress_widget->setMaximum(100);
m_status_progress_widget->hide();

m_status_verbose_widget = new QLabel(m_ui.statusBar);
m_status_verbose_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_status_verbose_widget->setFixedHeight(16);
m_status_verbose_widget->hide();

m_status_renderer_widget = new QLabel(m_ui.statusBar);
m_status_renderer_widget->setFixedHeight(16);
m_status_renderer_widget->setFixedSize(65, 16);
m_status_renderer_widget->hide();

m_status_resolution_widget = new QLabel(m_ui.statusBar);
m_status_resolution_widget->setFixedHeight(16);
m_status_resolution_widget->setFixedSize(75, 16);
m_status_resolution_widget->hide();

m_status_fps_widget = new QLabel(m_ui.statusBar);
m_status_fps_widget->setFixedHeight(16);
m_status_fps_widget->setMinimumWidth(60);
m_status_fps_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_fps_widget->hide();

m_status_vps_widget = new QLabel(m_ui.statusBar);
m_status_vps_widget->setFixedHeight(16);
m_status_vps_widget->setMinimumWidth(60);
m_status_vps_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_vps_widget->hide();

m_status_speed_widget = new QLabel(m_ui.statusBar);
m_status_speed_widget->setFixedHeight(16);
m_status_speed_widget->setMinimumWidth(130);
m_status_speed_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_speed_widget->hide();

m_settings_toolbar_menu = new QMenu(m_ui.toolBar);
m_settings_toolbar_menu->addAction(m_ui.actionSettings);
m_settings_toolbar_menu->addAction(m_ui.actionViewGameProperties);
Expand All @@ -257,6 +212,14 @@ void MainWindow::setupAdditionalUi()
connect(action, &QAction::triggered, [scale]() { g_emu_thread->requestDisplaySize(static_cast<float>(scale)); });
}

const bool show_game_grid = Host::GetBaseBoolSettingValue("UI", "GameListGridView", false);
updateGameGridActions(show_game_grid);

m_game_list_widget = new GameListWidget(getContentParent());
m_game_list_widget->initialize();
m_ui.actionGridViewShowTitles->setChecked(m_game_list_widget->getShowGridCoverTitles());
m_ui.mainContainer->addWidget(m_game_list_widget);

updateEmulationActions(false, false, false);
updateDisplayRelatedActions(false, false, false);

Expand Down Expand Up @@ -292,6 +255,121 @@ void MainWindow::setupAdditionalUi()
#endif
}

void MainWindow::setupStatusBarWidgets()
{
const bool status_bar_visible = Host::GetBaseBoolSettingValue("UI", "ShowStatusBar", true);
m_ui.actionViewStatusBar->setChecked(status_bar_visible);
m_ui.statusBar->setVisible(status_bar_visible);

m_status_progress_widget = new QProgressBar(m_ui.statusBar);
m_status_progress_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_progress_widget->setFixedSize(140, 16);
m_status_progress_widget->setMinimum(0);
m_status_progress_widget->setMaximum(100);
m_status_progress_widget->hide();

m_status_verbose_widget = new QLabel(m_ui.statusBar);
m_status_verbose_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_status_verbose_widget->setFixedHeight(20);
m_status_verbose_widget->hide();

m_status_renderer_widget = new QLabel(m_ui.statusBar);
m_status_renderer_widget->setMinimumWidth(80);
m_status_renderer_widget->setAlignment(Qt::AlignCenter);
m_status_renderer_widget->setFixedHeight(20);
m_status_renderer_widget->hide();

m_status_resolution_widget = new QLabel(m_ui.statusBar);
m_status_resolution_widget->setMinimumWidth(100);
m_status_resolution_widget->setAlignment(Qt::AlignCenter);
m_status_resolution_widget->setFixedHeight(20);
m_status_resolution_widget->hide();

m_status_volume_widget = new QToolButton(m_ui.statusBar);
m_status_volume_widget->setAutoRaise(true);
m_status_volume_widget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
m_status_volume_widget->setIcon(QIcon::fromTheme(QStringLiteral("volume-up-line")));
m_status_volume_widget->setPopupMode(QToolButton::InstantPopup);
m_status_volume_widget->setMinimumWidth(100);
m_status_volume_widget->setFixedHeight(20);

m_status_volume_menu = new QMenu(m_status_volume_widget);
m_status_volume_menu->addAction(tr("Toggle Mute"), []() {
Host::RunOnCPUThread([]() {
if (VMManager::HasValidVM())
{
const bool new_muted = !SPU2::IsOutputMuted();
if (SPU2::SetOutputMuted(new_muted))
SPU2::SetOutputMuted(new_muted);
}
});
});
m_status_volume_menu->addSeparator();

m_status_volume_slider = new QSlider(Qt::Horizontal, m_status_volume_menu);
m_status_volume_slider->setRange(0, 100);
m_status_volume_slider->setFixedWidth(120);
m_status_volume_slider->setValue(Host::GetBaseIntSettingValue("SPU2/Output", "StandardVolume", 100));
connect(m_status_volume_slider, &QSlider::valueChanged, [](int value) {
Copy link
Copy Markdown
Member

@chaoticgd chaoticgd May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not happy about how this interacts with the settings system.

For example, if you tick "Mute All Sound" in the settings dialog, the unmute button in the status bar stops working, but if you drag on the slider in the status bar it starts playing sound again. And then if you change another audio setting like Buffer Size it mutes itself again.

If we're going to go forward with having it only apply temporarily we're going to need to decide what the source of truth should be.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hotkey just disables the volume options when it's muted from the settings dialog. So I guess you'd want to do that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to go forward with having it only apply temporarily we're going to need to decide what the source of truth should be.

Now that i think about it, i feel like its just easier to sync the volume on the status bar to the one on the settings, instead of making it ephemeral.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that i think about it, i feel like its just easier to sync the volume on the status bar to the one on the settings, instead of making it ephemeral.

I like that idea. One thing to consider is how it will interact with per-game settings. Maybe have a "Per Game" checkbox in the volume drop down? Seems slightly clunky though. Any ideas?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's too clunky imo, maybe have a priority level where if the user has a custom per-game settings volume, the dropdown reflects that instead over the global volume? What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea that sounds good.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I think about it, I would like to have at least some kind of indication if it's modifying the per-game settings or the global settings. Otherwise if will behave differently in different situations, and it won't be clear what those situations are. How about something like this?

Screenshot_20260508_183859

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah i've been thinking of what indicator to use for per-game settings, originally going with an asterisk to indicate per-game but yours works better.

Host::RunOnCPUThread([value]() {
if (VMManager::HasValidVM())
{
SPU2::SetOutputMuted(false);
SPU2::SetOutputVolume(static_cast<u32>(value));
}
});
});

QWidget* container = new QWidget(m_status_volume_menu);
QHBoxLayout* layout = new QHBoxLayout(container);
layout->setContentsMargins(8, 4, 8, 4);
layout->addWidget(m_status_volume_slider);

QWidgetAction* slider_action = new QWidgetAction(m_status_volume_menu);
slider_action->setDefaultWidget(container);
m_status_volume_menu->addAction(slider_action);
m_status_volume_widget->setMenu(m_status_volume_menu);
m_status_volume_widget->hide();

m_status_speed_widget = new QToolButton(m_ui.statusBar);
m_status_speed_widget->setAutoRaise(true);
m_status_speed_widget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
m_status_speed_widget->setIcon(QIcon::fromTheme(QStringLiteral("dashboard-line")));
m_status_speed_widget->setPopupMode(QToolButton::InstantPopup);
m_status_speed_widget->setFixedHeight(20);
m_status_speed_widget->setMinimumWidth(60);
m_status_speed_widget->hide();

m_status_speed_menu = new QMenu(m_status_speed_widget);
m_status_speed_menu->addAction(tr("Unlimited"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Unlimited); }); });
m_status_speed_menu->addAction(tr("Turbo"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Turbo); }); });
m_status_speed_menu->addAction(tr("Slow-Motion"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Slomo); }); });
m_status_speed_menu->addAction(tr("Normal"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Nominal); }); });
m_status_speed_widget->setMenu(m_status_speed_menu);
m_status_speed_widget->hide();

m_status_gpu_widget = new QLabel(m_ui.statusBar);
m_status_gpu_widget->setAlignment(Qt::AlignCenter);
m_status_gpu_widget->setFixedHeight(20);
m_status_gpu_widget->setMinimumWidth(60);
m_status_gpu_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_gpu_widget->hide();

m_status_fps_widget = new QLabel(m_ui.statusBar);
m_status_fps_widget->setAlignment(Qt::AlignCenter);
m_status_fps_widget->setFixedHeight(20);
m_status_fps_widget->setMinimumWidth(60);
m_status_fps_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_fps_widget->hide();

m_status_vps_widget = new QLabel(m_ui.statusBar);
m_status_vps_widget->setAlignment(Qt::AlignCenter);
m_status_vps_widget->setFixedHeight(20);
m_status_vps_widget->setMinimumWidth(60);
m_status_vps_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_vps_widget->hide();
}

void MainWindow::connectSignals()
{
connect(m_ui.actionStartFile, &QAction::triggered, this, &MainWindow::onStartFileActionTriggered);
Expand Down Expand Up @@ -432,7 +510,7 @@ void MainWindow::connectSignals()
connect(m_game_list_widget, &GameListWidget::addGameDirectoryRequested, this,
[this]() { getSettingsWindow()->getGameListSettingsWidget()->addSearchDirectory(this); });

createRendererSwitchMenu();
populateRendererMenu(m_ui.menuDebugSwitchRenderer);
}

void MainWindow::connectVMThreadSignals(EmuThread* thread)
Expand Down Expand Up @@ -468,7 +546,7 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
connect(m_ui.actionReloadPatches, &QAction::triggered, thread, &EmuThread::reloadPatches);
}

void MainWindow::createRendererSwitchMenu()
void MainWindow::populateRendererMenu(QMenu* menu)
{
static constexpr const GSRendererType renderers[] = {
GSRendererType::Auto,
Expand All @@ -487,6 +565,7 @@ void MainWindow::createRendererSwitchMenu()
GSRendererType::SW,
GSRendererType::Null,
};

const GSRendererType current_renderer = static_cast<GSRendererType>(
Host::GetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(GSRendererType::Auto)));

Expand All @@ -498,15 +577,14 @@ void MainWindow::createRendererSwitchMenu()
QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer)), switch_renderer_group);
action->setCheckable(true);
action->setChecked(current_renderer == renderer);
connect(action,
&QAction::triggered, [renderer] {
Host::SetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(renderer));
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings();
});
connect(action, &QAction::triggered, [renderer] {
Host::SetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(renderer));
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings();
});
}

m_ui.menuDebugSwitchRenderer->addActions(switch_renderer_group->actions());
menu->addActions(switch_renderer_group->actions());
}

void MainWindow::recreate()
Expand Down Expand Up @@ -997,11 +1075,55 @@ void MainWindow::updateStatusBarWidgetVisibility()
};

Update(m_status_verbose_widget, s_vm_valid, 1);
Update(m_status_speed_widget, s_vm_valid, 0);
Update(m_status_volume_widget, s_vm_valid, 0);
Update(m_status_renderer_widget, s_vm_valid, 0);
Update(m_status_resolution_widget, s_vm_valid, 0);
Update(m_status_gpu_widget, s_vm_valid, 0);
Update(m_status_fps_widget, s_vm_valid, 0);
Update(m_status_vps_widget, s_vm_valid, 0);
Update(m_status_speed_widget, s_vm_valid, 0);
}

void MainWindow::setStatusRendererText(const QString& text)
{
m_status_renderer_widget->setText(text);
}

void MainWindow::setStatusResolutionText(const QString& text)
{
m_status_resolution_widget->setText(text);
}

void MainWindow::setStatusVolumeText(const QString& text, int volume, bool muted)
{
m_status_volume_widget->setText(text);
m_status_volume_widget->setIcon(QIcon::fromTheme(muted ? QStringLiteral("volume-mute-line") : QStringLiteral("volume-up-line")));

if (m_status_volume_slider && !m_status_volume_slider->isSliderDown())
{
QSignalBlocker blocker(m_status_volume_slider);
m_status_volume_slider->setValue(volume);
}
}

void MainWindow::setStatusGPUText(const QString& text)
{
m_status_gpu_widget->setText(text);
}

void MainWindow::setStatusFPSText(const QString& text)
{
m_status_fps_widget->setText(text);
}

void MainWindow::setStatusVPSText(const QString& text)
{
m_status_vps_widget->setText(text);
}

void MainWindow::setStatusSpeedText(const QString& text)
{
m_status_speed_widget->setText(text);
}

void MainWindow::updateWindowTitle()
Expand Down Expand Up @@ -2182,6 +2304,8 @@ void MainWindow::onVMStopped()
m_last_fps_status = empty_string;
m_status_renderer_widget->setText(empty_string);
m_status_resolution_widget->setText(empty_string);
m_status_volume_widget->setText(empty_string);
m_status_gpu_widget->setText(empty_string);
m_status_fps_widget->setText(empty_string);
m_status_vps_widget->setText(empty_string);
m_status_speed_widget->setText(empty_string);
Expand Down
Loading
Loading