diff --git a/dist/qt_themes/colorful/style.qrc b/dist/qt_themes/colorful/style.qrc index b8d47460f..2866c69bd 100644 --- a/dist/qt_themes/colorful/style.qrc +++ b/dist/qt_themes/colorful/style.qrc @@ -13,6 +13,6 @@ icons/256x256/plus_folder.png - style.qss + ../default/style.qss diff --git a/dist/qt_themes/colorful/style.qss b/dist/qt_themes/colorful/style.qss deleted file mode 100644 index 413fc81da..000000000 --- a/dist/qt_themes/colorful/style.qss +++ /dev/null @@ -1,4 +0,0 @@ -/* - This file is intentionally left blank. - We do not want to apply any stylesheet for colorful, only icons. -*/ diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc index cf011680f..6da475316 100644 --- a/dist/qt_themes/default/default.qrc +++ b/dist/qt_themes/default/default.qrc @@ -1,33 +1,22 @@ icons/index.theme - icons/16x16/checked.png - icons/16x16/failed.png - icons/16x16/connected.png - icons/16x16/disconnected.png - icons/16x16/connected_notification.png - icons/16x16/lock.png - icons/48x48/bad_folder.png - icons/48x48/chip.png - icons/48x48/folder.png - icons/48x48/no_avatar.png - icons/48x48/plus.png - icons/48x48/sd_card.png - icons/256x256/citra.png - icons/256x256/plus_folder.png + + style.qss + diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss new file mode 100644 index 000000000..e467504b6 --- /dev/null +++ b/dist/qt_themes/default/style.qss @@ -0,0 +1,14 @@ +QPushButton#3DOptionStatusBarButton { + color: #A5A5A5; + font-weight: bold; + border: 1px solid transparent; + background-color: transparent; + padding: 0px 3px 0px 3px; + text-align: center; + min-width: 60px; + min-height: 20px; +} + +QPushButton#3DOptionStatusBarButton:hover { + border: 1px solid #76797C; +} diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index a5eee211e..318f78b7a 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss @@ -522,13 +522,12 @@ QToolButton#qt_toolbar_ext_button { QPushButton { color: #eff0f1; - border-width: 1px; - border-color: #54575B; - border-style: solid; - padding: 6px 4px; + border: 1px solid #54575B; border-radius: 2px; + padding: 5px 0px 5px 0px; outline: none; min-width: 100px; + min-height: 13px; background-color: #232629; } @@ -1237,3 +1236,18 @@ QPlainTextEdit:disabled { TouchScreenPreview { qproperty-dotHighlightColor: #3daee9; } + +QPushButton#3DOptionStatusBarButton { + color: #A5A5A5; + font-weight: bold; + border: 1px solid transparent; + background-color: transparent; + padding: 0px 3px 0px 3px; + text-align: center; + min-width: 60px; + min-height: 20px; +} + +QPushButton#3DOptionStatusBarButton:hover { + border: 1px solid #76797C; +} diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 968821a6a..5e5d41e83 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -55,14 +55,16 @@ 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 {{ +const std::array Config::default_hotkeys {{ {QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, {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}}, {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, {QStringLiteral("Exit Citra"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}}, {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, + {QStringLiteral("Increase 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl++"), Qt::ApplicationShortcut}}, {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}}, {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}}, @@ -74,6 +76,7 @@ const std::array Config::default_hotkeys {{ {QStringLiteral("Save to Oldest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), Qt::WindowShortcut}}, {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, {QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}}, + {QStringLiteral("Toggle 3D"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+3"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Per-Game Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, {QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}}, diff --git a/src/citra_qt/configuration/config.h b/src/citra_qt/configuration/config.h index 7f1855d72..d28983a7a 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 b35337f3f..d11e71997 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -117,6 +117,7 @@ __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; #endif constexpr int default_mouse_timeout = 2500; +constexpr int num_options_3d = 5; /** * "Callouts" are one-time instructional messages shown to the user. In the config settings, there @@ -202,6 +203,7 @@ GMainWindow::GMainWindow() ConnectMenuEvents(); ConnectWidgetEvents(); + Connect3DStateEvents(); LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc); @@ -299,7 +301,6 @@ void GMainWindow::InitializeWidgets() { // Create status bar message_label = new QLabel(); // Configured separately for left alignment - message_label->setVisible(false); message_label->setFrameStyle(QFrame::NoFrame); message_label->setContentsMargins(4, 0, 4, 0); message_label->setAlignment(Qt::AlignLeft); @@ -324,10 +325,26 @@ void GMainWindow::InitializeWidgets() { label->setVisible(false); label->setFrameStyle(QFrame::NoFrame); label->setContentsMargins(4, 0, 4, 0); - statusBar()->addPermanentWidget(label, 0); + statusBar()->addPermanentWidget(label); } - statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); - statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); + + option_3d_button = new QPushButton(); + option_3d_button->setObjectName(QStringLiteral("3DOptionStatusBarButton")); + option_3d_button->setFocusPolicy(Qt::NoFocus); + option_3d_button->setToolTip(tr("Indicates the current 3D setting. Click to toggle.")); + + factor_3d_slider = new QSlider(Qt::Orientation::Horizontal, this); + factor_3d_slider->setStyleSheet(QStringLiteral("QSlider { padding: 4px; }")); + factor_3d_slider->setToolTip(tr("Current 3D factor while 3D is enabled.")); + factor_3d_slider->setRange(0, 100); + + Update3DState(); + statusBar()->insertPermanentWidget(0, option_3d_button); + statusBar()->insertPermanentWidget(1, factor_3d_slider); + + statusBar()->addPermanentWidget(multiplayer_state->GetStatusText()); + statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon()); + statusBar()->setVisible(true); // Removes an ugly inner border from the status bar widgets under Linux @@ -575,6 +592,35 @@ void GMainWindow::InitializeHotkeys() { }); connect_shortcut(QStringLiteral("Mute Audio"), [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); + + connect_shortcut(QStringLiteral("Toggle 3D"), &GMainWindow::Toggle3D); + + // 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 + static constexpr u16 FACTOR_3D_STEP = 5; + connect_shortcut(QStringLiteral("Decrease 3D Factor"), [this] { + const auto factor_3d = Settings::values.factor_3d.GetValue(); + if (factor_3d > 0) { + if (factor_3d % FACTOR_3D_STEP != 0) { + Settings::values.factor_3d = factor_3d - (factor_3d % FACTOR_3D_STEP); + } else { + Settings::values.factor_3d = factor_3d - FACTOR_3D_STEP; + } + UpdateStatusBar(); + } + }); + connect_shortcut(QStringLiteral("Increase 3D Factor"), [this] { + const auto factor_3d = Settings::values.factor_3d.GetValue(); + if (factor_3d < 100) { + if (factor_3d % FACTOR_3D_STEP != 0) { + Settings::values.factor_3d = + factor_3d + FACTOR_3D_STEP - (factor_3d % FACTOR_3D_STEP); + } else { + Settings::values.factor_3d = factor_3d + FACTOR_3D_STEP; + } + UpdateStatusBar(); + } + }); } void GMainWindow::ShowUpdaterWidgets() { @@ -805,6 +851,12 @@ void GMainWindow::UpdateMenuState() { } } +void GMainWindow::Connect3DStateEvents() { + connect(option_3d_button, &QPushButton::clicked, this, &GMainWindow::Toggle3D); + connect(factor_3d_slider, qOverload(&QSlider::valueChanged), this, + [](int value) { Settings::values.factor_3d = value; }); +} + void GMainWindow::OnDisplayTitleBars(bool show) { QList widgets = findChildren(); @@ -1219,7 +1271,6 @@ void GMainWindow::ShutdownGame() { // Disable status bar updates status_bar_update_timer.stop(); - message_label->setVisible(false); message_label_used_for_movie = false; emu_speed_label->setVisible(false); game_fps_label->setVisible(false); @@ -1900,6 +1951,7 @@ void GMainWindow::OnConfigure() { setMouseTracking(false); } UpdateSecondaryWindowVisibility(); + Update3DState(); } else { Settings::values.input_profiles = old_input_profiles; Settings::values.touch_from_button_maps = old_touch_from_button_maps; @@ -2160,22 +2212,18 @@ void GMainWindow::UpdateStatusBar() { const auto play_mode = Core::Movie::GetInstance().GetPlayMode(); if (play_mode == Core::Movie::PlayMode::Recording) { message_label->setText(tr("Recording %1").arg(current)); - message_label->setVisible(true); message_label_used_for_movie = true; ui->action_Save_Movie->setEnabled(true); } else if (play_mode == Core::Movie::PlayMode::Playing) { message_label->setText(tr("Playing %1 / %2").arg(current, total)); - message_label->setVisible(true); message_label_used_for_movie = true; ui->action_Save_Movie->setEnabled(false); } else if (play_mode == Core::Movie::PlayMode::MovieFinished) { message_label->setText(tr("Movie Finished")); - message_label->setVisible(true); message_label_used_for_movie = true; ui->action_Save_Movie->setEnabled(false); } else if (message_label_used_for_movie) { // Clear the label if movie was just closed message_label->setText(QString{}); - message_label->setVisible(false); message_label_used_for_movie = false; ui->action_Save_Movie->setEnabled(false); } @@ -2197,6 +2245,18 @@ void GMainWindow::UpdateStatusBar() { emu_frametime_label->setVisible(true); } +void GMainWindow::Update3DState() { + static const std::array options_3d = {tr("Off"), tr("Side by Side"), tr("Anaglyph"), + tr("Interlaced"), tr("Reverse Interlaced")}; + + option_3d_button->setText( + tr("3D: %1").arg(options_3d[static_cast(Settings::values.render_3d.GetValue())])); + + factor_3d_slider->setValue(Settings::values.factor_3d.GetValue()); + factor_3d_slider->setVisible(Settings::values.render_3d.GetValue() != + Settings::StereoRenderOption::Off); +} + void GMainWindow::HideMouseCursor() { if (emu_thread == nullptr || !UISettings::values.hide_mouse.GetValue()) { mouse_hide_timer.stop(); @@ -2299,7 +2359,6 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det if (emu_thread) { emu_thread->SetRunning(true); message_label->setText(status_message); - message_label->setVisible(true); message_label_used_for_movie = false; } } @@ -2309,6 +2368,12 @@ void GMainWindow::OnMenuAboutCitra() { about.exec(); } +void GMainWindow::Toggle3D() { + Settings::values.render_3d = static_cast( + (static_cast(Settings::values.render_3d.GetValue()) + 1) % num_options_3d); + Update3DState(); +} + bool GMainWindow::ConfirmClose() { if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) return true; @@ -2418,8 +2483,18 @@ void GMainWindow::UpdateUITheme() { QStringList theme_paths(default_theme_paths); if (is_default_theme || current_theme.isEmpty()) { - qApp->setStyleSheet({}); - setStyleSheet({}); + const QString theme_uri(QStringLiteral(":default/style.qss")); + QFile f(theme_uri); + if (f.open(QFile::ReadOnly | QFile::Text)) { + QTextStream ts(&f); + qApp->setStyleSheet(ts.readAll()); + setStyleSheet(ts.readAll()); + } else { + LOG_ERROR(Frontend, + "Unable to open default stylesheet, falling back to empty stylesheet"); + qApp->setStyleSheet({}); + setStyleSheet({}); + } theme_paths.append(default_icons); QIcon::setThemeName(default_icons); } else { diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 4b3fa0d36..47ee3cfb0 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -40,6 +40,8 @@ template class QFutureWatcher; class QLabel; class QProgressBar; +class QPushButton; +class QSlider; class RegistersWidget; class Updater; class WaitTreeWidget; @@ -118,6 +120,7 @@ private: void RestoreUIState(); void ConnectWidgetEvents(); + void Connect3DStateEvents(); void ConnectMenuEvents(); void UpdateMenuState(); @@ -225,6 +228,7 @@ private slots: void OnStopVideoDumping(); #endif void OnCoreError(Core::System::ResultStatus, std::string); + void Toggle3D(); /// Called whenever a user selects Help->About Citra void OnMenuAboutCitra(); void OnUpdateFound(bool found, bool error); @@ -236,6 +240,7 @@ private slots: private: Q_INVOKABLE void OnMoviePlaybackCompleted(); void UpdateStatusBar(); + void Update3DState(); void LoadTranslation(); void UpdateWindowTitle(); void UpdateUISettings(); @@ -259,6 +264,8 @@ private: QLabel* emu_speed_label = nullptr; QLabel* game_fps_label = nullptr; QLabel* emu_frametime_label = nullptr; + QPushButton* option_3d_button = nullptr; + QSlider* factor_3d_slider = nullptr; QTimer status_bar_update_timer; bool message_label_used_for_movie = false;