From 9f67f72d3cf2d33231a520d6ea784122dd5358cc Mon Sep 17 00:00:00 2001 From: KamFretoZ <14798312+kamfretoz@users.noreply.github.com> Date: Mon, 13 Apr 2026 19:45:19 +0700 Subject: [PATCH] Qt: Revamp the status bar --- pcsx2-qt/MainWindow.cpp | 350 +++++++++++++++--- pcsx2-qt/MainWindow.h | 28 +- pcsx2-qt/QtHost.cpp | 120 ++++-- pcsx2-qt/QtHost.h | 5 + .../icons/black/svg/dashboard-line.svg | 1 + .../icons/black/svg/fast-forward-line.svg | 3 + .../resources/icons/black/svg/slow-mo.svg | 1 + .../resources/icons/black/svg/speed-line.svg | 3 + .../icons/black/svg/volume-mute-line.svg | 1 + .../icons/white/svg/dashboard-line.svg | 3 + .../icons/white/svg/fast-forward-line.svg | 3 + .../resources/icons/white/svg/slow-mo.svg | 1 + .../resources/icons/white/svg/speed-line.svg | 3 + .../icons/white/svg/volume-mute-line.svg | 1 + pcsx2-qt/resources/resources.qrc | 10 + pcsx2/ImGui/ImGuiOverlays.cpp | 2 +- pcsx2/SPU2/spu2.cpp | 18 +- 17 files changed, 453 insertions(+), 100 deletions(-) create mode 100644 pcsx2-qt/resources/icons/black/svg/dashboard-line.svg create mode 100644 pcsx2-qt/resources/icons/black/svg/fast-forward-line.svg create mode 100644 pcsx2-qt/resources/icons/black/svg/slow-mo.svg create mode 100644 pcsx2-qt/resources/icons/black/svg/speed-line.svg create mode 100644 pcsx2-qt/resources/icons/black/svg/volume-mute-line.svg create mode 100644 pcsx2-qt/resources/icons/white/svg/dashboard-line.svg create mode 100644 pcsx2-qt/resources/icons/white/svg/fast-forward-line.svg create mode 100644 pcsx2-qt/resources/icons/white/svg/slow-mo.svg create mode 100644 pcsx2-qt/resources/icons/white/svg/speed-line.svg create mode 100644 pcsx2-qt/resources/icons/white/svg/volume-mute-line.svg diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index 0ccddb0246..7586b8145a 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -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" @@ -54,8 +57,11 @@ #include #include #include +#include #include #include +#include +#include #ifdef _WIN32 #include "common/RedtapeWindows.h" @@ -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); @@ -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); @@ -257,6 +212,14 @@ void MainWindow::setupAdditionalUi() connect(action, &QAction::triggered, [scale]() { g_emu_thread->requestDisplaySize(static_cast(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); @@ -292,6 +255,216 @@ 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->setContentsMargins(10, 0, 10, 0); + m_status_verbose_widget->setFixedHeight(20); + m_status_verbose_widget->setMinimumWidth(20); + m_status_verbose_widget->hide(); + + m_status_renderer_widget = new QLabel(m_ui.statusBar); + m_status_renderer_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_status_renderer_widget->setContentsMargins(10, 0, 10, 0); + 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->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_status_resolution_widget->setContentsMargins(10, 0, 10, 0); + 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->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_status_volume_widget->setContentsMargins(10, 0, 10, 0); + 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->setFixedHeight(20); + + m_status_volume_menu = new QMenu(m_status_volume_widget); + + m_status_volume_per_game_action = new QAction(tr("Adjust Per-Game"), m_status_volume_menu); + m_status_volume_per_game_action->setCheckable(true); + + connect(m_status_volume_menu, &QMenu::aboutToShow, this, [this]() { + auto lock = Host::GetSettingsLock(); + SettingsInterface* game_layer = Host::Internal::GetGameSettingsLayer(); + m_status_volume_per_game_action->setEnabled(game_layer != nullptr && QtHost::IsVMValid()); + m_status_volume_per_game_action->setChecked(game_layer && + (game_layer->ContainsValue("SPU2/Output", "StandardVolume") || game_layer->ContainsValue("SPU2/Output", "OutputMuted"))); + }); + + connect(m_status_volume_per_game_action, &QAction::triggered, this, [this](bool checked) { + m_status_volume_slider_applied = true; + applyStatusBarVolumeChanges(std::nullopt, false, checked); + }); + + m_status_volume_toggle_mute_action = new QAction(tr("Toggle Mute"), m_status_volume_menu); + m_status_volume_toggle_mute_action->setIcon(QIcon::fromTheme(QStringLiteral("volume-mute-line"))); + + connect(m_status_volume_toggle_mute_action, &QAction::triggered, this, [this]() { + m_status_volume_slider_applied = true; + applyStatusBarVolumeChanges(std::nullopt, true, std::nullopt); + }); + + m_status_volume_menu->addAction(m_status_volume_per_game_action); + m_status_volume_menu->addAction(m_status_volume_toggle_mute_action); + 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::GetIntSettingValue("SPU2/Output", "StandardVolume", 100)); + connect(m_status_volume_slider, &QSlider::valueChanged, this, [this](int value) { + Host::RunOnCPUThread([value]() { + if (!VMManager::HasValidVM()) + return; + EmuConfig.SPU2.StandardVolume = static_cast(value); + SPU2::SetOutputVolume(static_cast(value)); + }); + if (m_status_volume_slider->isSliderDown()) + setStatusVolumeText(m_status_volume_muted ? tr("Volume: Muted") : tr("Volume: %1%").arg(value), value, m_status_volume_muted); + }); + connect(m_status_volume_slider, &QSlider::sliderReleased, this, [this]() { + m_status_volume_slider_applied = true; + applyStatusBarVolumeChanges(m_status_volume_slider->value(), false); + }); + connect(m_status_volume_menu, &QMenu::aboutToHide, this, [this]() { + if (!m_status_volume_slider_applied) + applyStatusBarVolumeChanges(m_status_volume_slider->value(), false); + m_status_volume_slider_applied = false; + }); + + 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->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_status_speed_widget->setContentsMargins(10, 0, 10, 0); + m_status_speed_widget->setAutoRaise(true); + m_status_speed_widget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_status_speed_widget->setPopupMode(QToolButton::InstantPopup); + m_status_speed_widget->setFixedHeight(20); + 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->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_status_gpu_widget->setContentsMargins(10, 0, 10, 0); + m_status_gpu_widget->setAlignment(Qt::AlignCenter); + m_status_gpu_widget->setFixedHeight(20); + m_status_gpu_widget->hide(); + + m_status_fps_widget = new QLabel(m_ui.statusBar); + m_status_fps_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_status_fps_widget->setContentsMargins(10, 0, 10, 0); + m_status_fps_widget->setAlignment(Qt::AlignCenter); + m_status_fps_widget->setFixedHeight(20); + m_status_fps_widget->hide(); + + m_status_vps_widget = new QLabel(m_ui.statusBar); + m_status_vps_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_status_vps_widget->setContentsMargins(10, 0, 10, 0); + m_status_vps_widget->setAlignment(Qt::AlignCenter); + m_status_vps_widget->setFixedHeight(20); + m_status_vps_widget->hide(); +} + +void MainWindow::applyStatusBarVolumeChanges(std::optional volume, bool toggle_mute, std::optional override_per_game) +{ + const bool use_per_game = override_per_game.value_or(m_status_volume_per_game_action->isChecked()); + Host::RunOnCPUThread([this, volume, toggle_mute, use_per_game]() { + int target_vol = 100; + bool target_mute = false; + + { + auto lock = Host::GetSettingsLock(); + SettingsInterface* per_game_settings = Host::Internal::GetGameSettingsLayer(); + SettingsInterface* global_settings = Host::Internal::GetBaseSettingsLayer(); + SettingsInterface* active_setting = (use_per_game && per_game_settings) ? per_game_settings : global_settings; + + if (active_setting) + { + if (!active_setting->GetIntValue("SPU2/Output", "StandardVolume", &target_vol) && global_settings) + global_settings->GetIntValue("SPU2/Output", "StandardVolume", &target_vol); + if (!active_setting->GetBoolValue("SPU2/Output", "OutputMuted", &target_mute) && global_settings) + global_settings->GetBoolValue("SPU2/Output", "OutputMuted", &target_mute); + } + + target_vol = volume.value_or(target_vol); + if (toggle_mute) + target_mute = !(VMManager::HasValidVM() ? SPU2::IsOutputMuted() : target_mute); + + if (active_setting) + { + active_setting->SetIntValue("SPU2/Output", "StandardVolume", target_vol); + active_setting->SetBoolValue("SPU2/Output", "OutputMuted", target_mute); + } + + if (per_game_settings && active_setting == per_game_settings) + QtHost::SaveGameSettings(per_game_settings, true); + else + { + if (per_game_settings) + { + const bool had_per_game_volume = + per_game_settings->ContainsValue("SPU2/Output", "StandardVolume") || + per_game_settings->ContainsValue("SPU2/Output", "OutputMuted"); + per_game_settings->DeleteValue("SPU2/Output", "StandardVolume"); + per_game_settings->DeleteValue("SPU2/Output", "OutputMuted"); + if (had_per_game_volume) + QtHost::SaveGameSettings(per_game_settings, true); + } + Host::CommitBaseSettingChanges(); + } + } + + if (VMManager::HasValidVM()) + { + EmuConfig.SPU2.StandardVolume = static_cast(target_vol); + EmuConfig.SPU2.OutputMuted = target_mute; + SPU2::SetOutputMuted(target_mute); + SPU2::SetOutputVolume(static_cast(target_vol)); + } + + QTimer::singleShot(0, this, [this, target_vol, target_mute]() { + setStatusVolumeText(target_mute ? tr("Volume: Muted") : tr("Volume: %1%").arg(target_vol), target_vol, target_mute); + }); + }); +} + void MainWindow::connectSignals() { connect(m_ui.actionStartFile, &QAction::triggered, this, &MainWindow::onStartFileActionTriggered); @@ -496,12 +669,11 @@ 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(renderer)); - Host::CommitBaseSettingChanges(); - g_emu_thread->applySettings(); - }); + connect(action, &QAction::triggered, [renderer] { + Host::SetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast(renderer)); + Host::CommitBaseSettingChanges(); + g_emu_thread->applySettings(); + }); } m_ui.menuDebugSwitchRenderer->addActions(switch_renderer_group->actions()); @@ -1019,13 +1191,15 @@ void MainWindow::updateDisplayRelatedActions(bool has_surface, bool render_to_ma void MainWindow::updateStatusBarWidgetVisibility() { auto Update = [this](QWidget* widget, bool visible, int stretch) { + if (widget->isVisible() == visible) + return; + if (widget->isVisible()) { m_ui.statusBar->removeWidget(widget); widget->hide(); } - - if (visible) + else { m_ui.statusBar->addPermanentWidget(widget, stretch); widget->show(); @@ -1035,9 +1209,63 @@ void MainWindow::updateStatusBarWidgetVisibility() Update(m_status_verbose_widget, s_vm_valid, 1); 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); + Update(m_status_volume_widget, s_vm_valid, 0); +} + +void MainWindow::setStatusVerboseText(const QString& text) +{ + m_status_verbose_widget->setText(text); +} + +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); + if (muted != m_status_volume_muted) + { + m_status_volume_muted = muted; + m_status_volume_widget->setIcon(QIcon::fromTheme(muted ? QStringLiteral("volume-mute-line") : QStringLiteral("volume-up-line"))); + m_status_volume_toggle_mute_action->setIcon(QIcon::fromTheme(muted ? QStringLiteral("volume-up-line") : QStringLiteral("volume-mute-line"))); + } + + if (!muted && !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() @@ -2218,6 +2446,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); diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h index 8fae33574e..b6ec363716 100644 --- a/pcsx2-qt/MainWindow.h +++ b/pcsx2-qt/MainWindow.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include @@ -96,11 +98,13 @@ public: /// Accessors for the status bar widgets, updated by the emulation thread. __fi QLabel* getStatusVerboseWidget() const { return m_status_verbose_widget; } + __fi QToolButton* getStatusSpeedWidget() const { return m_status_speed_widget; } + __fi QToolButton* getStatusVolumeWidget() const { return m_status_volume_widget; } __fi QLabel* getStatusRendererWidget() const { return m_status_renderer_widget; } __fi QLabel* getStatusResolutionWidget() const { return m_status_resolution_widget; } + __fi QLabel* getStatusGPUWidget() const { return m_status_gpu_widget; } __fi QLabel* getStatusFPSWidget() const { return m_status_fps_widget; } __fi QLabel* getStatusVPSWidget() const { return m_status_vps_widget; } - __fi QLabel* getStatusSpeedWidget() const { return m_status_speed_widget; } /// Rescans a single file. NOTE: Happens on UI thread. void rescanFile(const std::string& path); @@ -125,6 +129,15 @@ public Q_SLOTS: void reportStateLoadError(const QString& message, std::optional slot, bool backup); void reportStateSaveError(const QString& message, std::optional slot); + void setStatusVerboseText(const QString& text); + void setStatusRendererText(const QString& text); + void setStatusResolutionText(const QString& text); + void setStatusVolumeText(const QString& text, int volume, bool muted); + void setStatusGPUText(const QString& text); + void setStatusFPSText(const QString& text); + void setStatusVPSText(const QString& text); + void setStatusSpeedText(const QString& text); + void runOnUIThread(const std::function& func); void requestReset(); bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool default_save_to_state = true); @@ -227,6 +240,8 @@ protected: private: void setupAdditionalUi(); + void setupStatusBarWidgets(); + void applyStatusBarVolumeChanges(std::optional volume, bool toggle_mute, std::optional override_per_game = std::nullopt); void connectSignals(); void createRendererSwitchMenu(); void recreate(); @@ -309,14 +324,23 @@ private: QProgressBar* m_status_progress_widget = nullptr; QLabel* m_status_verbose_widget = nullptr; QLabel* m_status_renderer_widget = nullptr; + QToolButton* m_status_volume_widget = nullptr; + QMenu* m_status_volume_menu = nullptr; + QAction* m_status_volume_per_game_action = nullptr; + QAction* m_status_volume_toggle_mute_action = nullptr; + QSlider* m_status_volume_slider = nullptr; + QLabel* m_status_gpu_widget = nullptr; QLabel* m_status_fps_widget = nullptr; QLabel* m_status_vps_widget = nullptr; - QLabel* m_status_speed_widget = nullptr; + QToolButton* m_status_speed_widget = nullptr; + QMenu* m_status_speed_menu = nullptr; QLabel* m_status_resolution_widget = nullptr; QMenu* m_settings_toolbar_menu = nullptr; bool m_display_created = false; + bool m_status_volume_muted = false; + bool m_status_volume_slider_applied = false; bool m_display_is_exclusive_fullscreen = false; bool m_relative_mouse_mode = false; bool m_hide_mouse_cursor = false; diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index 91305b51a1..11d63c345f 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -433,10 +433,16 @@ void EmuThread::run() void EmuThread::destroyVM() { m_last_speed = 0.0f; + m_last_gpu_usage = 0.0f; m_last_game_fps = 0.0f; m_last_video_fps = 0.0f; m_last_internal_width = 0; m_last_internal_height = 0; + m_last_upscale = 0.0f; + m_last_volume = 0; + m_last_muted = false; + m_last_renderer = GSRendererType::Auto; + m_last_limiter_mode = LimiterModeType::Nominal; m_was_paused_by_focus_loss = false; VMManager::Shutdown(m_save_state_on_shutdown); m_save_state_on_shutdown = false; @@ -970,8 +976,15 @@ void EmuThread::updatePerformanceMetrics(bool force) if (!g_main_window) return; + const s32 slot = SaveStateSelectorUI::GetCurrentSlot(); + u32 volume = 0; + bool muted = false; + if (VMManager::HasValidVM()) { + volume = SPU2::GetOutputVolume(); + muted = SPU2::IsOutputMuted(); + QString gs_stat; if (m_verbose_status) { @@ -980,58 +993,90 @@ void EmuThread::updatePerformanceMetrics(bool force) if (THREAD_VU1) { - gs_stat = tr("Slot: %1 | Volume: %2% | %3 | EE: %4% | VU: %5% | GS: %6%") - .arg(SaveStateSelectorUI::GetCurrentSlot()) - .arg(SPU2::GetOutputVolume()) - .arg(gs_stat_str.c_str()) - .arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0) - .arg(PerformanceMetrics::GetVUThreadUsage(), 0, 'f', 0) - .arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0); + gs_stat = tr("Slot: %1 | %2 | EE: %3% | VU: %4% | GS: %5%") + .arg(slot) + .arg(gs_stat_str.c_str()) + .arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0) + .arg(PerformanceMetrics::GetVUThreadUsage(), 0, 'f', 0) + .arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0); } else { - gs_stat = tr("Slot: %1 | Volume: %2% | %3 | EE: %4% | GS: %5%") - .arg(SaveStateSelectorUI::GetCurrentSlot()) - .arg(SPU2::GetOutputVolume()) - .arg(gs_stat_str.c_str()) - .arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0) - .arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0); + gs_stat = tr("Slot: %1 | %2 | EE: %3% | GS: %4%") + .arg(slot) + .arg(gs_stat_str.c_str()) + .arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0) + .arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0); } } - QMetaObject::invokeMethod(g_main_window->getStatusVerboseWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, gs_stat)); + QMetaObject::invokeMethod(g_main_window, "setStatusVerboseText", Qt::QueuedConnection, Q_ARG(const QString&, gs_stat)); } const GSRendererType renderer = GSGetCurrentRenderer(); // Reading from GS thread, therefore racey, but it's just visual. + const float upscale = EmuConfig.GS.UpscaleMultiplier; const float speed = std::round(PerformanceMetrics::GetSpeed()); + const LimiterModeType limiter_mode = VMManager::GetLimiterMode(); + const float gpu_usage = std::round(PerformanceMetrics::GetGPUUsage()); const float gfps = std::round(PerformanceMetrics::GetInternalFPS()); const float vfps = std::round(PerformanceMetrics::GetFPS()); int iwidth, iheight; GSgetInternalResolution(&iwidth, &iheight); - if (iwidth != m_last_internal_width || iheight != m_last_internal_height || speed != m_last_speed || gfps != m_last_game_fps || - vfps != m_last_video_fps || renderer != m_last_renderer || force) + if (iwidth != m_last_internal_width || iheight != m_last_internal_height || upscale != m_last_upscale || + speed != m_last_speed || gpu_usage != m_last_gpu_usage || gfps != m_last_game_fps || vfps != m_last_video_fps || renderer != m_last_renderer || + volume != m_last_volume || muted != m_last_muted || force) { + + if (volume != m_last_volume || muted != m_last_muted || force) + { + QString vol_text = tr("Volume: %1%").arg(volume); + if (muted) + vol_text = tr("Volume: Muted"); + QMetaObject::invokeMethod(g_main_window, "setStatusVolumeText", Qt::QueuedConnection, + Q_ARG(const QString&, vol_text), Q_ARG(int, static_cast(volume)), Q_ARG(bool, muted)); + m_last_volume = volume; + m_last_muted = muted; + } + if (renderer != m_last_renderer || force) { - QMetaObject::invokeMethod(g_main_window->getStatusRendererWidget(), "setText", Qt::QueuedConnection, - Q_ARG(const QString&, QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer)))); + QString renderer_name = QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer)); + if (EmuConfig.GS.Renderer == GSRendererType::Auto && renderer != GSRendererType::Auto) + renderer_name = tr("Auto (%1)").arg(renderer_name); + + QMetaObject::invokeMethod(g_main_window, "setStatusRendererText", Qt::QueuedConnection, + Q_ARG(const QString&, renderer_name)); m_last_renderer = renderer; } - if (iwidth != m_last_internal_width || iheight != m_last_internal_height || force) + if (iwidth != m_last_internal_width || iheight != m_last_internal_height || upscale != m_last_upscale || force) { QString text; if (iwidth == 0 || iheight == 0) text = tr("No Image"); else - text = tr("%1x%2").arg(iwidth).arg(iheight); + text = tr("%1x%2 (%3x)").arg(iwidth).arg(iheight).arg(upscale, 0, 'g', 3); QMetaObject::invokeMethod( - g_main_window->getStatusResolutionWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, text)); + g_main_window, "setStatusResolutionText", Qt::QueuedConnection, Q_ARG(const QString&, text)); m_last_internal_width = iwidth; m_last_internal_height = iheight; + m_last_upscale = upscale; + } + + if (gpu_usage != m_last_gpu_usage || force) + { + QString text; + if (gpu_usage == 0) + text = tr("GPU: N/A"); + else + text = tr("GPU: %1%").arg(gpu_usage, 0, 'f', 0); + + QMetaObject::invokeMethod(g_main_window, "setStatusGPUText", Qt::QueuedConnection, + Q_ARG(const QString&, text)); + m_last_gpu_usage = gpu_usage; } if (gfps != m_last_game_fps || force) @@ -1042,24 +1087,37 @@ void EmuThread::updatePerformanceMetrics(bool force) else text = tr("FPS: %1").arg(gfps, 0, 'f', 0); - QMetaObject::invokeMethod(g_main_window->getStatusFPSWidget(), "setText", Qt::QueuedConnection, + QMetaObject::invokeMethod(g_main_window, "setStatusFPSText", Qt::QueuedConnection, Q_ARG(const QString&, text)); m_last_game_fps = gfps; } if (vfps != m_last_video_fps || force) { - QMetaObject::invokeMethod(g_main_window->getStatusVPSWidget(), "setText", Qt::QueuedConnection, - Q_ARG(const QString&, tr("VPS: %1 ").arg(vfps, 0, 'f', 0))); + QMetaObject::invokeMethod(g_main_window, "setStatusVPSText", Qt::QueuedConnection, + Q_ARG(const QString&, vfps == 0 ? tr("VPS: N/A") : tr("VPS: %1").arg(vfps, 0, 'f', 0))); m_last_video_fps = vfps; - - if (speed != m_last_speed || force) - { - QMetaObject::invokeMethod(g_main_window->getStatusSpeedWidget(), "setText", Qt::QueuedConnection, - Q_ARG(const QString&, tr("Speed: %1% ").arg(speed, 0, 'f', 0))); - m_last_speed = speed; - } } + + if (speed != m_last_speed || force) + { + QMetaObject::invokeMethod(g_main_window, "setStatusSpeedText", Qt::QueuedConnection, + Q_ARG(const QString&, tr("Speed: %1%").arg(speed, 0, 'f', 0))); + m_last_speed = speed; + } + + if (limiter_mode != m_last_limiter_mode || force) + { + QMetaObject::invokeMethod(g_main_window, [limiter_mode]() { + g_main_window->getStatusSpeedWidget()->setIcon(QIcon::fromTheme( + limiter_mode == LimiterModeType::Turbo ? QStringLiteral("fast-forward-line") : + limiter_mode == LimiterModeType::Slomo ? QStringLiteral("slow-mo") : + limiter_mode == LimiterModeType::Unlimited ? QStringLiteral("speed-line") : + QStringLiteral("dashboard-line"))); + }, Qt::QueuedConnection); + m_last_limiter_mode = limiter_mode; + } + } } diff --git a/pcsx2-qt/QtHost.h b/pcsx2-qt/QtHost.h index a094390c68..fb733a40eb 100644 --- a/pcsx2-qt/QtHost.h +++ b/pcsx2-qt/QtHost.h @@ -212,11 +212,16 @@ private: bool m_was_paused_by_focus_loss = false; float m_last_speed = 0.0f; + float m_last_gpu_usage = 0.0f; float m_last_game_fps = 0.0f; float m_last_video_fps = 0.0f; int m_last_internal_width = 0; int m_last_internal_height = 0; + float m_last_upscale = 0.0f; + u32 m_last_volume = 0; + bool m_last_muted = false; GSRendererType m_last_renderer = GSRendererType::Auto; + LimiterModeType m_last_limiter_mode = LimiterModeType::Nominal; }; extern EmuThread* g_emu_thread; diff --git a/pcsx2-qt/resources/icons/black/svg/dashboard-line.svg b/pcsx2-qt/resources/icons/black/svg/dashboard-line.svg new file mode 100644 index 0000000000..7d123fea93 --- /dev/null +++ b/pcsx2-qt/resources/icons/black/svg/dashboard-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pcsx2-qt/resources/icons/black/svg/fast-forward-line.svg b/pcsx2-qt/resources/icons/black/svg/fast-forward-line.svg new file mode 100644 index 0000000000..725b3de444 --- /dev/null +++ b/pcsx2-qt/resources/icons/black/svg/fast-forward-line.svg @@ -0,0 +1,3 @@ + + + diff --git a/pcsx2-qt/resources/icons/black/svg/slow-mo.svg b/pcsx2-qt/resources/icons/black/svg/slow-mo.svg new file mode 100644 index 0000000000..3ba747e2bc --- /dev/null +++ b/pcsx2-qt/resources/icons/black/svg/slow-mo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pcsx2-qt/resources/icons/black/svg/speed-line.svg b/pcsx2-qt/resources/icons/black/svg/speed-line.svg new file mode 100644 index 0000000000..c5136f56d7 --- /dev/null +++ b/pcsx2-qt/resources/icons/black/svg/speed-line.svg @@ -0,0 +1,3 @@ + + + diff --git a/pcsx2-qt/resources/icons/black/svg/volume-mute-line.svg b/pcsx2-qt/resources/icons/black/svg/volume-mute-line.svg new file mode 100644 index 0000000000..df10f66aad --- /dev/null +++ b/pcsx2-qt/resources/icons/black/svg/volume-mute-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pcsx2-qt/resources/icons/white/svg/dashboard-line.svg b/pcsx2-qt/resources/icons/white/svg/dashboard-line.svg new file mode 100644 index 0000000000..e8e265b6ea --- /dev/null +++ b/pcsx2-qt/resources/icons/white/svg/dashboard-line.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/pcsx2-qt/resources/icons/white/svg/fast-forward-line.svg b/pcsx2-qt/resources/icons/white/svg/fast-forward-line.svg new file mode 100644 index 0000000000..83610de9d9 --- /dev/null +++ b/pcsx2-qt/resources/icons/white/svg/fast-forward-line.svg @@ -0,0 +1,3 @@ + + + diff --git a/pcsx2-qt/resources/icons/white/svg/slow-mo.svg b/pcsx2-qt/resources/icons/white/svg/slow-mo.svg new file mode 100644 index 0000000000..11f053935c --- /dev/null +++ b/pcsx2-qt/resources/icons/white/svg/slow-mo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pcsx2-qt/resources/icons/white/svg/speed-line.svg b/pcsx2-qt/resources/icons/white/svg/speed-line.svg new file mode 100644 index 0000000000..46923a5354 --- /dev/null +++ b/pcsx2-qt/resources/icons/white/svg/speed-line.svg @@ -0,0 +1,3 @@ + + + diff --git a/pcsx2-qt/resources/icons/white/svg/volume-mute-line.svg b/pcsx2-qt/resources/icons/white/svg/volume-mute-line.svg new file mode 100644 index 0000000000..21f17f53e8 --- /dev/null +++ b/pcsx2-qt/resources/icons/white/svg/volume-mute-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pcsx2-qt/resources/resources.qrc b/pcsx2-qt/resources/resources.qrc index ec1f0ca907..8b34790cb2 100644 --- a/pcsx2-qt/resources/resources.qrc +++ b/pcsx2-qt/resources/resources.qrc @@ -18,6 +18,7 @@ icons/black/svg/close-line.svg icons/black/svg/controller-line.svg icons/black/svg/controller-strike-line.svg + icons/black/svg/dashboard-line.svg icons/black/svg/debug-step-into-line.svg icons/black/svg/debug-step-out-line.svg icons/black/svg/debug-step-over-line.svg @@ -32,6 +33,7 @@ icons/black/svg/eject-line.svg icons/black/svg/emulation-line.svg icons/black/svg/eyetoy-line.svg + icons/black/svg/fast-forward-line.svg icons/black/svg/file-add-line.svg icons/black/svg/file-line.svg icons/black/svg/file-list-line.svg @@ -90,6 +92,8 @@ icons/black/svg/settings-3-line.svg icons/black/svg/shut-down-line.svg icons/black/svg/singstar-line.svg + icons/black/svg/slow-mo.svg + icons/black/svg/speed-line.svg icons/black/svg/tools-line.svg icons/black/svg/train-line.svg icons/black/svg/trance-vibrator-line.svg @@ -97,6 +101,7 @@ icons/black/svg/trophy-line.svg icons/black/svg/tv-2-line.svg icons/black/svg/usb-fill.svg + icons/black/svg/volume-mute-line.svg icons/black/svg/volume-up-line.svg icons/black/svg/warning-line.svg icons/black/svg/wheel-line.svg @@ -123,6 +128,7 @@ icons/white/svg/close-line.svg icons/white/svg/controller-line.svg icons/white/svg/controller-strike-line.svg + icons/white/svg/dashboard-line.svg icons/white/svg/debug-step-into-line.svg icons/white/svg/debug-step-out-line.svg icons/white/svg/debug-step-over-line.svg @@ -137,6 +143,7 @@ icons/white/svg/eject-line.svg icons/white/svg/emulation-line.svg icons/white/svg/eyetoy-line.svg + icons/white/svg/fast-forward-line.svg icons/white/svg/file-add-line.svg icons/white/svg/file-line.svg icons/white/svg/file-list-line.svg @@ -198,6 +205,8 @@ icons/white/svg/settings-3-line.svg icons/white/svg/shut-down-line.svg icons/white/svg/singstar-line.svg + icons/white/svg/slow-mo.svg + icons/white/svg/speed-line.svg icons/white/svg/tools-line.svg icons/white/svg/train-line.svg icons/white/svg/trance-vibrator-line.svg @@ -205,6 +214,7 @@ icons/white/svg/trophy-line.svg icons/white/svg/tv-2-line.svg icons/white/svg/usb-fill.svg + icons/white/svg/volume-mute-line.svg icons/white/svg/volume-up-line.svg icons/white/svg/warning-line.svg icons/white/svg/wheel-line.svg diff --git a/pcsx2/ImGui/ImGuiOverlays.cpp b/pcsx2/ImGui/ImGuiOverlays.cpp index b2ac49844f..b1309911d9 100644 --- a/pcsx2/ImGui/ImGuiOverlays.cpp +++ b/pcsx2/ImGui/ImGuiOverlays.cpp @@ -912,7 +912,7 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa if (GSConfig.HWAccurateAlphaTest) APPEND("AAT "); - + if (GSConfig.HWAA1) APPEND("AA1 "); diff --git a/pcsx2/SPU2/spu2.cpp b/pcsx2/SPU2/spu2.cpp index 0190015517..2c7a5f805b 100644 --- a/pcsx2/SPU2/spu2.cpp +++ b/pcsx2/SPU2/spu2.cpp @@ -150,12 +150,18 @@ void SPU2::UpdateSampleRate() u32 SPU2::GetOutputVolume() { - return s_output_stream->GetOutputVolume(); + return s_output_stream ? s_output_stream->GetOutputVolume() : 0; } void SPU2::SetOutputVolume(u32 volume) { - s_output_stream->SetOutputVolume(volume); + if (VMManager::GetTargetSpeed() == 1.0f) + s_standard_volume = volume; + else + s_fast_forward_volume = volume; + + if (s_output_stream) + SPU2::UpdateOutputVolume(); } float SPU2::GetNominalRate() @@ -198,9 +204,9 @@ void SPU2::SaveOutputVolume() if (!s_output_muted) { if (VMManager::GetTargetSpeed() == 1.0f) - s_standard_volume = s_output_stream->GetOutputVolume(); + s_standard_volume = GetOutputVolume(); else - s_fast_forward_volume = s_output_stream->GetOutputVolume(); + s_fast_forward_volume = GetOutputVolume(); } } @@ -268,12 +274,12 @@ void SPU2::OnTargetSpeedChanged() { if (VMManager::GetTargetSpeed() == 1.0f) { - s_fast_forward_volume = s_output_stream->GetOutputVolume(); + s_fast_forward_volume = GetOutputVolume(); s_output_stream->SetOutputVolume(s_standard_volume); } else { - s_standard_volume = s_output_stream->GetOutputVolume(); + s_standard_volume = GetOutputVolume(); s_output_stream->SetOutputVolume(s_fast_forward_volume); } }