diff --git a/dist/qt_themes/colorful/style.qrc b/dist/qt_themes/colorful/style.qrc
index b8d47460fa..2866c69bda 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 413fc81da7..0000000000
--- 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 cf011680f2..6da475316c 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 0000000000..e467504b6b
--- /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 a5eee211ee..318f78b7a0 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 968821a6a5..5e5d41e833 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 7f1855d728..d28983a7ac 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 b35337f3fb..d11e719974 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 4b3fa0d366..47ee3cfb00 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;