diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss index 4f1e99f52a..f4505cccf9 100644 --- a/dist/qt_themes/default/style.qss +++ b/dist/qt_themes/default/style.qss @@ -12,18 +12,19 @@ QPushButton#GraphicsAPIStatusBarButton:hover { border: 1px solid #76797C; } -QPushButton#3DOptionStatusBarButton { - color: #A5A5A5; - font-weight: bold; +QPushButton#TogglableStatusBarButton { + color: #959595; border: 1px solid transparent; background-color: transparent; padding: 0px 3px 0px 3px; text-align: center; - min-width: 60px; - min-height: 20px; } -QPushButton#3DOptionStatusBarButton:hover { +QPushButton#TogglableStatusBarButton:checked { + color: #00FF00; +} + +QPushButton#TogglableStatusBarButton:hover { border: 1px solid #76797C; } diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index 238a411824..a05eb5e1a7 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss @@ -1,19 +1,3 @@ -QPushButton#TogglableStatusBarButton { - color: #959595; - border: 1px solid transparent; - background-color: transparent; - padding: 0px 3px 0px 3px; - text-align: center; -} - -QPushButton#TogglableStatusBarButton:checked { - color: palette(text); -} - -QPushButton#TogglableStatusBarButton:hover { - border: 1px solid #76797C; -} - QPushButton#GraphicsAPIStatusBarButton { color: #656565; border: 1px solid transparent; @@ -26,6 +10,23 @@ QPushButton#GraphicsAPIStatusBarButton:hover { border: 1px solid #76797C; } +QPushButton#TogglableStatusBarButton { + min-width: 0px; + color: #656565; + border: 1px solid transparent; + background-color: transparent; + padding: 0px 3px 0px 3px; + text-align: center; +} + +QPushButton#TogglableStatusBarButton:checked { + color: #00FF00; +} + +QPushButton#TogglableStatusBarButton:hover { + border: 1px solid #76797C; +} + QToolTip { border: 1px solid #76797C; background-color: #5A7566; diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 4611b87f54..b00f17664f 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -54,8 +54,11 @@ const std::array, Settings::NativeAnalog::NumAnalogs> Config: // This must be in alphabetical order according to action name as it must have the same order as // UISetting::values.shortcuts, which is alphabetically ordered. // clang-format off -const std::array Config::default_hotkeys {{ - {QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, +const std::array Config::default_hotkeys {{ + {QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, + {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, + {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}}, + {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}}, {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}}, {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, {QStringLiteral("Decrease 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+-"), Qt::ApplicationShortcut}}, @@ -68,7 +71,6 @@ const std::array Config::default_hotkeys {{ {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}}, {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}}, {QStringLiteral("Load from Newest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}}, - {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, {QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}}, {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, {QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}}, diff --git a/src/citra_qt/configuration/config.h b/src/citra_qt/configuration/config.h index 5727450558..27be93711a 100644 --- a/src/citra_qt/configuration/config.h +++ b/src/citra_qt/configuration/config.h @@ -26,7 +26,7 @@ public: static const std::array default_buttons; static const std::array, Settings::NativeAnalog::NumAnalogs> default_analogs; - static const std::array default_hotkeys; + static const std::array default_hotkeys; private: void Initialize(const std::string& config_name); diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 0c723330ab..302ebc1f7d 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -420,6 +420,38 @@ void GMainWindow::InitializeWidgets() { statusBar()->insertPermanentWidget(0, graphics_api_button); + volume_popup = new QWidget(this); + volume_popup->setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | Qt::Popup); + volume_popup->setLayout(new QVBoxLayout()); + volume_popup->setMinimumWidth(200); + + volume_slider = new QSlider(Qt::Horizontal); + volume_slider->setObjectName(QStringLiteral("volume_slider")); + volume_slider->setMaximum(100); + volume_slider->setPageStep(5); + connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) { + Settings::values.audio_muted = false; + const auto value = static_cast(percentage) / volume_slider->maximum(); + Settings::values.volume.SetValue(value); + UpdateVolumeUI(); + }); + volume_popup->layout()->addWidget(volume_slider); + + volume_button = new QPushButton(); + volume_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); + volume_button->setFocusPolicy(Qt::NoFocus); + volume_button->setCheckable(true); + UpdateVolumeUI(); + connect(volume_button, &QPushButton::clicked, this, [&] { + UpdateVolumeUI(); + volume_popup->setVisible(!volume_popup->isVisible()); + QRect rect = volume_button->geometry(); + QPoint bottomLeft = statusBar()->mapToGlobal(rect.topLeft()); + bottomLeft.setY(bottomLeft.y() - volume_popup->geometry().height()); + volume_popup->setGeometry(QRect(bottomLeft, QSize(rect.width(), rect.height()))); + }); + statusBar()->insertPermanentWidget(1, volume_button); + statusBar()->addPermanentWidget(multiplayer_state->GetStatusText()); statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon()); @@ -672,8 +704,10 @@ void GMainWindow::InitializeHotkeys() { } UpdateStatusBar(); }); - connect_shortcut(QStringLiteral("Mute Audio"), - [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); + + connect_shortcut(QStringLiteral("Audio Mute/Unmute"), &GMainWindow::OnMute); + connect_shortcut(QStringLiteral("Audio Volume Down"), &GMainWindow::OnDecreaseVolume); + connect_shortcut(QStringLiteral("Audio Volume Up"), &GMainWindow::OnIncreaseVolume); // We use "static" here in order to avoid capturing by lambda due to a MSVC bug, which makes the // variable hold a garbage value after this function exits @@ -1874,7 +1908,7 @@ void GMainWindow::OnStartGame() { #endif UpdateSaveStates(); - UpdateAPIIndicator(); + UpdateStatusButtons(); } void GMainWindow::OnRestartGame() { @@ -1911,7 +1945,7 @@ void GMainWindow::OnStopGame() { ShutdownGame(); graphics_api_button->setEnabled(true); Settings::RestoreGlobalState(false); - UpdateAPIIndicator(); + UpdateStatusButtons(); } void GMainWindow::OnLoadComplete() { @@ -2152,7 +2186,7 @@ void GMainWindow::OnConfigure() { } UpdateSecondaryWindowVisibility(); UpdateBootHomeMenuState(); - UpdateAPIIndicator(); + UpdateStatusButtons(); } else { Settings::values.input_profiles = old_input_profiles; Settings::values.touch_from_button_maps = old_touch_from_button_maps; @@ -2582,6 +2616,57 @@ void GMainWindow::ShowMouseCursor() { } } +void GMainWindow::OnMute() { + Settings::values.audio_muted = !Settings::values.audio_muted; + UpdateVolumeUI(); +} + +void GMainWindow::OnDecreaseVolume() { + Settings::values.audio_muted = false; + const auto current_volume = + static_cast(Settings::values.volume.GetValue() * volume_slider->maximum()); + int step = 5; + if (current_volume <= 30) { + step = 2; + } + if (current_volume <= 6) { + step = 1; + } + const auto value = + static_cast(std::max(current_volume - step, 0)) / volume_slider->maximum(); + Settings::values.volume.SetValue(value); + UpdateVolumeUI(); +} + +void GMainWindow::OnIncreaseVolume() { + Settings::values.audio_muted = false; + const auto current_volume = + static_cast(Settings::values.volume.GetValue() * volume_slider->maximum()); + int step = 5; + if (current_volume < 30) { + step = 2; + } + if (current_volume < 6) { + step = 1; + } + const auto value = static_cast(current_volume + step) / volume_slider->maximum(); + Settings::values.volume.SetValue(value); + UpdateVolumeUI(); +} + +void GMainWindow::UpdateVolumeUI() { + const auto volume_value = + static_cast(Settings::values.volume.GetValue() * volume_slider->maximum()); + volume_slider->setValue(volume_value); + if (Settings::values.audio_muted) { + volume_button->setChecked(false); + volume_button->setText(tr("VOLUME: MUTE")); + } else { + volume_button->setChecked(true); + volume_button->setText(tr("VOLUME: %1%", "Volume percentage (e.g. 50%)").arg(volume_value)); + } +} + void GMainWindow::UpdateAPIIndicator(bool update) { static std::array graphics_apis = {QStringLiteral("SOFTWARE"), QStringLiteral("OPENGL"), QStringLiteral("VULKAN")}; @@ -2602,6 +2687,11 @@ void GMainWindow::UpdateAPIIndicator(bool update) { graphics_api_button->setStyleSheet(style_sheet); } +void GMainWindow::UpdateStatusButtons() { + UpdateAPIIndicator(); + UpdateVolumeUI(); +} + void GMainWindow::OnMouseActivity() { ShowMouseCursor(); } @@ -2903,6 +2993,8 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const QString& file_nam if (!is_powered_on) { config->Save(); } + + UpdateStatusButtons(); } void GMainWindow::OnMoviePlaybackCompleted() { diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 9aaea0a2a7..a89561ad2d 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -267,6 +267,10 @@ private slots: void OnLanguageChanged(const QString& locale); void OnMouseActivity(); + void OnDecreaseVolume(); + void OnIncreaseVolume(); + void OnMute(); + private: Q_INVOKABLE void OnMoviePlaybackCompleted(); void UpdateStatusBar(); @@ -279,7 +283,9 @@ private: void HideMouseCursor(); void ShowMouseCursor(); void OpenPerGameConfiguration(u64 title_id, const QString& file_name); + void UpdateVolumeUI(); void UpdateAPIIndicator(bool update = false); + void UpdateStatusButtons(); #ifdef __unix__ void SetGamemodeEnabled(bool state); #endif @@ -301,6 +307,9 @@ private: QLabel* game_fps_label = nullptr; QLabel* emu_frametime_label = nullptr; QPushButton* graphics_api_button = nullptr; + QPushButton* volume_button = nullptr; + QWidget* volume_popup = nullptr; + QSlider* volume_slider = nullptr; QTimer status_bar_update_timer; bool message_label_used_for_movie = false;