Add per game configuration options (#6187)

* common: Move settings to common from core.

- Removes a dependency on core and input_common from common.

* code: Wrap settings values

* Port from yuzu to allow per game settings

* citra_qt: Initial per-game settings dialog

* citra_qt: Use new API for read/save of config values

* citra_qt: Per game audio settings

* citra_qt: Per game graphics settings

* citra_qt: Per game system settings

* citra_qt: Per game general settings

* citra_qt: Document and run clang format

* citra_qt: Make icon smaller and centered

* citra_qt: Remove version number

* Not sure how to extract that, can always add it back later

* citra_qt: Wrap UISettings

* citra_qt: Fix unthottled fps setting

* citra_qt: Remove margin in emulation tab

* citra_qt: Implement some suggestions

* Bring back speed switch hotkey

* Allow configuration when game is running

* Rename/adjust UI stuff

* citra_qt: Fix build with separate windows

* citra_qt: Address feedback

* citra_qt: Log per-game settings before launching games

* citra_qt: Add shader cache options

* Also fix android build

* citra_qt: Add DLC menu option

* citra_qt: Run clang-format

* citra_qt: Adjust for time offset

* citra_qt: Implement suggestions

* Run clang-format

Co-authored-by: bunnei <bunneidev@gmail.com>
This commit is contained in:
GPUCode
2022-12-08 13:27:25 +02:00
committed by GitHub
parent f261daf2fa
commit 48ee112ceb
92 changed files with 3171 additions and 1546 deletions

View File

@@ -38,6 +38,8 @@ add_executable(citra-qt
configuration/config.cpp
configuration/config.h
configuration/configure.ui
configuration/configuration_shared.cpp
configuration/configuration_shared.h
configuration/configure_audio.cpp
configuration/configure_audio.h
configuration/configure_audio.ui
@@ -67,6 +69,9 @@ add_executable(citra-qt
configuration/configure_motion_touch.cpp
configuration/configure_motion_touch.h
configuration/configure_motion_touch.ui
configuration/configure_per_game.cpp
configuration/configure_per_game.h
configuration/configure_per_game.ui
configuration/configure_storage.cpp
configuration/configure_storage.h
configuration/configure_storage.ui

View File

@@ -15,11 +15,11 @@
#include "citra_qt/main.h"
#include "common/microprofile.h"
#include "common/scm_rev.h"
#include "common/settings.h"
#include "core/3ds.h"
#include "core/core.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/perf_stats.h"
#include "core/settings.h"
#include "input_common/keyboard.h"
#include "input_common/main.h"
#include "input_common/motion_emu.h"

File diff suppressed because it is too large Load Diff

View File

@@ -8,13 +8,16 @@
#include <memory>
#include <string>
#include <QVariant>
#include "core/settings.h"
#include "common/settings.h"
class QSettings;
class Config {
public:
Config();
enum class ConfigType : u32 { GlobalConfig, PerGameConfig };
explicit Config(const std::string& config_name = "qt-config",
ConfigType config_type = ConfigType::GlobalConfig);
~Config();
void Reload();
@@ -24,6 +27,8 @@ public:
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
private:
void Initialize(const std::string& config_name);
void ReadValues();
void ReadAudioValues();
void ReadCameraValues();
@@ -68,11 +73,78 @@ private:
void SaveWebServiceValues();
void SaveVideoDumpingValues();
/**
* Reads a setting from the qt_config.
*
* @param name The setting's identifier
* @param default_value The value to use when the setting is not already present in the config
*/
QVariant ReadSetting(const QString& name) const;
QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
/**
* Only reads a setting from the qt_config if the current config is a global config, or if the
* current config is a custom config and the setting is overriding the global setting. Otherwise
* it does nothing.
*
* @param setting The variable to be modified
* @param name The setting's identifier
* @param default_value The value to use when the setting is not already present in the config
*/
template <typename Type>
void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
/**
* Writes a setting to the qt_config.
*
* @param name The setting's idetentifier
* @param value Value of the setting
* @param default_value Default of the setting if not present in qt_config
* @param use_global Specifies if the custom or global config should be in use, for custom
* configs
*/
void WriteSetting(const QString& name, const QVariant& value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
bool use_global);
/**
* Reads a value from the qt_config and applies it to the setting, using its label and default
* value. If the config is a custom config, this will also read the global state of the setting
* and apply that information to it.
*
* @param The setting
*/
template <typename Type, bool ranged>
void ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& setting);
/**
* Sets a value to the qt_config using the setting's label and default value. If the config is a
* custom config, it will apply the global state, and the custom value if needed.
*
* @param The setting
*/
template <typename Type, bool ranged>
void WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& setting);
/**
* Reads a value from the qt_config using the setting's label and default value and applies the
* value to the setting.
*
* @param The setting
*/
template <typename Type, bool ranged>
void ReadBasicSetting(Settings::Setting<Type, ranged>& setting);
/** Sets a value from the setting in the qt_config using the setting's label and default value.
*
* @param The setting
*/
template <typename Type, bool ranged>
void WriteBasicSetting(const Settings::Setting<Type, ranged>& setting);
ConfigType type;
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
bool global;
};

View File

@@ -0,0 +1,95 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QCheckBox>
#include <QObject>
#include <QString>
#include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_per_game.h"
#include "common/settings.h"
void ConfigurationShared::ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting,
const QCheckBox* checkbox,
const CheckState& tracker) {
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
setting->SetValue(checkbox->checkState());
} else if (!Settings::IsConfiguringGlobal()) {
if (tracker == CheckState::Global) {
setting->SetGlobal(true);
} else {
setting->SetGlobal(false);
setting->SetValue(checkbox->checkState());
}
}
}
void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
const Settings::SwitchableSetting<bool>* setting) {
if (setting->UsingGlobal()) {
checkbox->setCheckState(Qt::PartiallyChecked);
} else {
checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
}
}
void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) {
if (highlighted) {
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
.arg(widget->objectName()));
} else {
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }")
.arg(widget->objectName()));
}
widget->show();
}
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox,
const Settings::SwitchableSetting<bool>& setting,
CheckState& tracker) {
if (setting.UsingGlobal()) {
tracker = CheckState::Global;
} else {
tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off;
}
SetHighlight(checkbox, tracker != CheckState::Global);
QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, setting, &tracker] {
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
static_cast<int>(CheckState::Count));
if (tracker == CheckState::Global) {
checkbox->setChecked(setting.GetValue(true));
}
SetHighlight(checkbox, tracker != CheckState::Global);
});
}
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, bool global, bool state,
bool global_state, CheckState& tracker) {
if (global) {
tracker = CheckState::Global;
} else {
tracker = (state == global_state) ? CheckState::On : CheckState::Off;
}
SetHighlight(checkbox, tracker != CheckState::Global);
QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, global_state, &tracker] {
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
static_cast<int>(CheckState::Count));
if (tracker == CheckState::Global) {
checkbox->setChecked(global_state);
}
SetHighlight(checkbox, tracker != CheckState::Global);
});
}
void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, int global) {
InsertGlobalItem(combobox, global);
QObject::connect(combobox, qOverload<int>(&QComboBox::activated), target,
[target](int index) { SetHighlight(target, index != 0); });
}
void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) {
const QString use_global_text =
ConfigurePerGame::tr("Use global configuration (%1)").arg(combobox->itemText(global_index));
combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
}

View File

@@ -0,0 +1,98 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <QCheckBox>
#include <QComboBox>
#include "common/settings.h"
namespace ConfigurationShared {
constexpr int USE_GLOBAL_INDEX =
0; ///< The index of the "Use global configuration" option in checkboxes
constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1;
constexpr int USE_GLOBAL_OFFSET = 2;
/// CheckBoxes require a tracker for their state since we emulate a tristate CheckBox
enum class CheckState {
Off, ///< Checkbox overrides to off/false
On, ///< Checkbox overrides to on/true
Global, ///< Checkbox defers to the global state
Count, ///< Simply the number of states, not a valid checkbox state
};
/**
* @brief ApplyPerGameSetting given a setting and a Qt UI element, properly applies a Setting
* taking into account the global/per-game check state. This is used for configuring checkboxes
* @param setting
* @param checkbox
* @param tracker
*/
void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox,
const CheckState& tracker);
/**
* @brief ApplyPerGameSetting given a setting and a Qt UI element, properly applies a Setting
* taking into account the global/per-game check state. This is used for both combo boxes
* as well as any other widget that is accompanied by a combo box in per-game settings.
* @param setting The setting class that stores the desired option
* @param combobox The Qt combo box that stores the value/per-game status
* @param transform A function that accepts the combo box index and transforms it to the
* desired settings value. When used with sliders/edit the user can ignore the input value
* and set a custom value this making this function useful for these widgets as well
*/
template <typename Type, bool ranged>
void ApplyPerGameSetting(Settings::SwitchableSetting<Type, ranged>* setting,
const QComboBox* combobox, auto transform) {
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
setting->SetValue(static_cast<Type>(transform(combobox->currentIndex())));
} else if (!Settings::IsConfiguringGlobal()) {
if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
setting->SetGlobal(true);
} else {
setting->SetGlobal(false);
setting->SetValue(static_cast<Type>(
transform(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET)));
}
}
}
/// Simpler version of ApplyPerGameSetting without a transform parameter
template <typename Type, bool ranged>
void ApplyPerGameSetting(Settings::SwitchableSetting<Type, ranged>* setting,
const QComboBox* combobox) {
const auto transform = [](s32 index) { return index; };
return ApplyPerGameSetting(setting, combobox, transform);
}
/// Sets a Qt UI element given a Settings::Setting
void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting);
template <typename Type, bool ranged>
void SetPerGameSetting(QComboBox* combobox,
const Settings::SwitchableSetting<Type, ranged>* setting) {
combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
: static_cast<int>(setting->GetValue()) +
ConfigurationShared::USE_GLOBAL_OFFSET);
}
/// Given a Qt widget sets the background color to indicate whether the setting
/// is per-game overriden (highlighted) or global (non-highlighted)
void SetHighlight(QWidget* widget, bool highlighted);
/// Sets up a QCheckBox like a tristate one, given a Setting
void SetColoredTristate(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>& setting,
CheckState& tracker);
void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state,
CheckState& tracker);
/// Sets up coloring of a QWidget `target` based on the state of a QComboBox, and calls
/// InsertGlobalItem
void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global);
/// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox
void InsertGlobalItem(QComboBox* combobox, int global_index);
} // namespace ConfigurationShared

View File

@@ -9,10 +9,11 @@
#endif
#include "audio_core/sink.h"
#include "audio_core/sink_details.h"
#include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_audio.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/frontend/mic.h"
#include "core/settings.h"
#include "ui_configure_audio.h"
#if defined(__APPLE__)
@@ -31,25 +32,30 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
ui->output_sink_combo_box->addItem(QString::fromUtf8(id));
}
ui->emulation_combo_box->addItem(tr("HLE (fast)"));
ui->emulation_combo_box->addItem(tr("LLE (accurate)"));
ui->emulation_combo_box->addItem(tr("LLE multi-core"));
ui->emulation_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn());
const bool is_running = Core::System::GetInstance().IsPoweredOn();
ui->emulation_combo_box->setEnabled(!is_running);
connect(ui->volume_slider, &QSlider::valueChanged, this,
&ConfigureAudio::SetVolumeIndicatorText);
ui->input_device_combo_box->clear();
ui->input_device_combo_box->addItem(tr("Default"));
#ifdef HAVE_CUBEB
for (const auto& device : AudioCore::ListCubebInputDevices()) {
ui->input_device_combo_box->addItem(QString::fromStdString(device));
}
#endif
connect(ui->input_type_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureAudio::UpdateAudioInputDevices);
ui->volume_label->setVisible(Settings::IsConfiguringGlobal());
ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal());
SetupPerGameUI();
SetConfiguration();
connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureAudio::UpdateAudioOutputDevices);
}
@@ -61,27 +67,35 @@ void ConfigureAudio::SetConfiguration() {
// The device list cannot be pre-populated (nor listed) until the output sink is known.
UpdateAudioOutputDevices(ui->output_sink_combo_box->currentIndex());
SetAudioDeviceFromDeviceID();
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
ui->volume_slider->setValue(
static_cast<int>(Settings::values.volume * ui->volume_slider->maximum()));
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
const s32 volume =
static_cast<s32>(Settings::values.volume.GetValue() * ui->volume_slider->maximum());
ui->volume_slider->setValue(volume);
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
int selection;
if (Settings::values.enable_dsp_lle) {
if (Settings::values.enable_dsp_lle_multithread) {
selection = 2;
if (!Settings::IsConfiguringGlobal()) {
if (Settings::values.volume.UsingGlobal()) {
ui->volume_combo_box->setCurrentIndex(0);
ui->volume_slider->setEnabled(false);
} else {
selection = 1;
ui->volume_combo_box->setCurrentIndex(1);
ui->volume_slider->setEnabled(true);
}
ConfigurationShared::SetHighlight(ui->volume_layout,
!Settings::values.volume.UsingGlobal());
ConfigurationShared::SetHighlight(ui->widget_emulation,
!Settings::values.audio_emulation.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->emulation_combo_box,
&Settings::values.audio_emulation);
} else {
selection = 0;
s32 selection = static_cast<s32>(Settings::values.audio_emulation.GetValue());
ui->emulation_combo_box->setCurrentIndex(selection);
}
ui->emulation_combo_box->setCurrentIndex(selection);
int index = static_cast<int>(Settings::values.mic_input_type);
s32 index = static_cast<s32>(Settings::values.mic_input_type.GetValue());
ui->input_type_combo_box->setCurrentIndex(index);
UpdateAudioInputDevices(index);
@@ -90,7 +104,7 @@ void ConfigureAudio::SetConfiguration() {
void ConfigureAudio::SetOutputSinkFromSinkID() {
int new_sink_index = 0;
const QString sink_id = QString::fromStdString(Settings::values.sink_id);
const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue());
for (int index = 0; index < ui->output_sink_combo_box->count(); index++) {
if (ui->output_sink_combo_box->itemText(index) == sink_id) {
new_sink_index = index;
@@ -104,7 +118,7 @@ void ConfigureAudio::SetOutputSinkFromSinkID() {
void ConfigureAudio::SetAudioDeviceFromDeviceID() {
int new_device_index = -1;
const QString device_id = QString::fromStdString(Settings::values.audio_device_id);
const QString device_id = QString::fromStdString(Settings::values.audio_device_id.GetValue());
for (int index = 0; index < ui->audio_device_combo_box->count(); index++) {
if (ui->audio_device_combo_box->itemText(index) == device_id) {
new_device_index = index;
@@ -120,24 +134,31 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
}
void ConfigureAudio::ApplyConfiguration() {
Settings::values.sink_id =
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
.toStdString();
Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
Settings::values.audio_device_id =
ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
.toStdString();
Settings::values.volume =
static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
Settings::values.enable_dsp_lle = ui->emulation_combo_box->currentIndex() != 0;
Settings::values.enable_dsp_lle_multithread = ui->emulation_combo_box->currentIndex() == 2;
Settings::values.mic_input_type =
static_cast<Settings::MicInputType>(ui->input_type_combo_box->currentIndex());
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
ui->toggle_audio_stretching, audio_stretching);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.audio_emulation,
ui->emulation_combo_box);
ConfigurationShared::ApplyPerGameSetting(
&Settings::values.volume, ui->volume_combo_box, [this](s32) {
return static_cast<float>(ui->volume_slider->value()) / ui->volume_slider->maximum();
});
if (ui->input_device_combo_box->currentIndex() == DEFAULT_INPUT_DEVICE_INDEX) {
Settings::values.mic_input_device = Frontend::Mic::default_device_name;
} else {
Settings::values.mic_input_device = ui->input_device_combo_box->currentText().toStdString();
if (Settings::IsConfiguringGlobal()) {
Settings::values.sink_id =
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
.toStdString();
Settings::values.audio_device_id =
ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
.toStdString();
Settings::values.mic_input_type =
static_cast<Settings::MicInputType>(ui->input_type_combo_box->currentIndex());
if (ui->input_device_combo_box->currentIndex() == DEFAULT_INPUT_DEVICE_INDEX) {
Settings::values.mic_input_device = Frontend::Mic::default_device_name;
} else {
Settings::values.mic_input_device =
ui->input_device_combo_box->currentText().toStdString();
}
}
}
@@ -157,12 +178,41 @@ void ConfigureAudio::UpdateAudioInputDevices(int index) {
AppleAuthorization::CheckAuthorizationForMicrophone();
}
#endif
if (Settings::values.mic_input_device != Frontend::Mic::default_device_name) {
if (Settings::values.mic_input_device.GetValue() != Frontend::Mic::default_device_name) {
ui->input_device_combo_box->setCurrentText(
QString::fromStdString(Settings::values.mic_input_device));
QString::fromStdString(Settings::values.mic_input_device.GetValue()));
}
}
void ConfigureAudio::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureAudio::SetupPerGameUI() {
if (Settings::IsConfiguringGlobal()) {
ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
return;
}
ui->output_sink_combo_box->setVisible(false);
ui->output_sink_label->setVisible(false);
ui->audio_device_combo_box->setVisible(false);
ui->audio_device_label->setVisible(false);
ui->input_type_label->setVisible(false);
ui->input_type_combo_box->setVisible(false);
ui->input_device_label->setVisible(false);
ui->input_device_combo_box->setVisible(false);
ui->microphone_layout->setVisible(false);
connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
ui->volume_slider->setEnabled(index == 1);
ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
});
ConfigurationShared::SetColoredComboBox(
ui->emulation_combo_box, ui->widget_emulation,
static_cast<u32>(Settings::values.audio_emulation.GetValue(true)));
ConfigurationShared::SetColoredTristate(
ui->toggle_audio_stretching, Settings::values.enable_audio_stretching, audio_stretching);
}

View File

@@ -11,6 +11,10 @@ namespace Ui {
class ConfigureAudio;
}
namespace ConfigurationShared {
enum class CheckState;
}
class ConfigureAudio : public QWidget {
Q_OBJECT
@@ -30,5 +34,8 @@ private:
void SetAudioDeviceFromDeviceID();
void SetVolumeIndicatorText(int percentage);
void SetupPerGameUI();
ConfigurationShared::CheckState audio_stretching;
std::unique_ptr<Ui::ConfigureAudio> ui;
};

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>329</width>
<height>344</height>
<width>696</width>
<height>527</height>
</rect>
</property>
<layout class="QVBoxLayout">
@@ -18,26 +18,53 @@
</property>
<layout class="QVBoxLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_emulation">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_emulation">
<property name="text">
<string>Emulation:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="emulation_combo_box"/>
</item>
</layout>
<widget class="QWidget" name="widget_emulation" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_emulation">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_emulation">
<property name="text">
<string>Emulation:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="emulation_combo_box">
<item>
<property name="text">
<string>HLE (fast)</string>
</property>
</item>
<item>
<property name="text">
<string>LLE (accurate)</string>
</property>
</item>
<item>
<property name="text">
<string>LLE multi-core</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout">
<layout class="QHBoxLayout" name="output_engine_layout">
<item>
<widget class="QLabel" name="label1">
<widget class="QLabel" name="output_sink_label">
<property name="text">
<string>Output Engine</string>
</property>
@@ -59,9 +86,9 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout">
<layout class="QHBoxLayout" name="audio_device_layout">
<item>
<widget class="QLabel" name="label2">
<widget class="QLabel" name="audio_device_label">
<property name="text">
<string>Audio Device</string>
</property>
@@ -73,72 +100,97 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Volume:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSlider" name="volume_slider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="pageStep">
<number>10</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="volume_indicator">
<property name="minimumSize">
<size>
<width>32</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>0 %</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
<widget class="QWidget" name="volume_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="volume_combo_box">
<item>
<property name="text">
<string>Use global volume</string>
</property>
</item>
<item>
<property name="text">
<string>Set volume:</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="volume_label">
<property name="text">
<string>Volume:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSlider" name="volume_slider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="pageStep">
<number>5</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="volume_indicator">
<property name="minimumSize">
<size>
<width>32</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>0 %</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<widget class="QGroupBox" name="microphone_layout">
<property name="title">
<string>Microphone</string>
</property>
@@ -146,7 +198,7 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<widget class="QLabel" name="input_type_label">
<property name="text">
<string>Input Type</string>
</property>
@@ -176,7 +228,7 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<widget class="QLabel" name="input_device_label">
<property name="text">
<string>Input Device</string>
</property>

View File

@@ -9,10 +9,10 @@
#include <QMessageBox>
#include <QWidget>
#include "citra_qt/configuration/configure_camera.h"
#include "common/settings.h"
#include "core/frontend/camera/factory.h"
#include "core/frontend/camera/interface.h"
#include "core/hle/service/cam/cam.h"
#include "core/settings.h"
#include "ui_configure_camera.h"
#if defined(__APPLE__)

View File

@@ -8,11 +8,9 @@
#include "citra_qt/debugger/console.h"
#include "citra_qt/uisettings.h"
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_debug.h"
ConfigureDebug::ConfigureDebug(QWidget* parent)
@@ -32,13 +30,13 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
ConfigureDebug::~ConfigureDebug() = default;
void ConfigureDebug::SetConfiguration() {
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port);
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue());
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue());
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue());
ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn());
ui->toggle_console->setChecked(UISettings::values.show_console);
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit);
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue());
}
void ConfigureDebug::ApplyConfiguration() {
@@ -48,7 +46,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
Debugger::ToggleConsole();
Log::Filter filter;
filter.ParseFilterString(Settings::values.log_filter);
filter.ParseFilterString(Settings::values.log_filter.GetValue());
Log::SetGlobalFilter(filter);
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
}

View File

@@ -7,11 +7,13 @@
#include "citra_qt/configuration/config.h"
#include "citra_qt/configuration/configure_dialog.h"
#include "citra_qt/hotkeys.h"
#include "core/settings.h"
#include "common/settings.h"
#include "ui_configure.h"
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, bool enable_web_config)
: QDialog(parent), ui(std::make_unique<Ui::ConfigureDialog>()), registry(registry) {
Settings::SetConfiguringGlobal(true);
ui->setupUi(this);
ui->hotkeysTab->Populate(registry);
ui->webTab->SetWebServiceConfigEnabled(enable_web_config);

View File

@@ -4,8 +4,8 @@
#include <QColorDialog>
#include "citra_qt/configuration/configure_enhancements.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_enhancements.h"
#include "video_core/renderer_opengl/post_processing_opengl.h"
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
@@ -21,7 +21,7 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
ui->layoutBox->setEnabled(!Settings::values.custom_layout);
ui->resolution_factor_combobox->setEnabled(Settings::values.use_hw_renderer);
ui->resolution_factor_combobox->setEnabled(Settings::values.use_hw_renderer.GetValue());
connect(ui->render_3d_combobox,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
@@ -50,27 +50,30 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
}
void ConfigureEnhancements::SetConfiguration() {
ui->resolution_factor_combobox->setCurrentIndex(Settings::values.resolution_factor);
ui->render_3d_combobox->setCurrentIndex(static_cast<int>(Settings::values.render_3d));
ui->factor_3d->setValue(Settings::values.factor_3d);
ui->mono_render_left_eye->setChecked(Settings::values.mono_render_left_eye);
updateShaders(Settings::values.render_3d);
ui->toggle_linear_filter->setChecked(Settings::values.filter_mode);
ui->resolution_factor_combobox->setCurrentIndex(Settings::values.resolution_factor.GetValue());
ui->render_3d_combobox->setCurrentIndex(
static_cast<int>(Settings::values.render_3d.GetValue()));
ui->factor_3d->setValue(Settings::values.factor_3d.GetValue());
ui->mono_render_left_eye->setChecked(Settings::values.mono_render_left_eye.GetValue());
updateShaders(Settings::values.render_3d.GetValue());
ui->toggle_linear_filter->setChecked(Settings::values.filter_mode.GetValue());
int tex_filter_idx = ui->texture_filter_combobox->findText(
QString::fromStdString(Settings::values.texture_filter_name));
QString::fromStdString(Settings::values.texture_filter_name.GetValue()));
if (tex_filter_idx == -1) {
ui->texture_filter_combobox->setCurrentIndex(0);
} else {
ui->texture_filter_combobox->setCurrentIndex(tex_filter_idx);
}
ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option));
ui->swap_screen->setChecked(Settings::values.swap_screen);
ui->upright_screen->setChecked(Settings::values.upright_screen);
ui->toggle_dump_textures->setChecked(Settings::values.dump_textures);
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures);
ui->toggle_preload_textures->setChecked(Settings::values.preload_textures);
bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
Settings::values.bg_blue);
ui->layout_combobox->setCurrentIndex(
static_cast<int>(Settings::values.layout_option.GetValue()));
ui->swap_screen->setChecked(Settings::values.swap_screen.GetValue());
ui->upright_screen->setChecked(Settings::values.upright_screen.GetValue());
ui->toggle_dump_textures->setChecked(Settings::values.dump_textures.GetValue());
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue());
ui->toggle_preload_textures->setChecked(Settings::values.preload_textures.GetValue());
bg_color =
QColor::fromRgbF(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue());
QPixmap pixmap(ui->bg_button->size());
pixmap.fill(bg_color);
const QIcon color_icon(pixmap);
@@ -93,7 +96,7 @@ void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_op
for (const auto& shader : OpenGL::GetPostProcessingShaderList(
stereo_option == Settings::StereoRenderOption::Anaglyph)) {
ui->shader_combobox->addItem(QString::fromStdString(shader));
if (Settings::values.pp_shader_name == shader)
if (Settings::values.pp_shader_name.GetValue() == shader)
ui->shader_combobox->setCurrentIndex(ui->shader_combobox->count() - 1);
}
}

View File

@@ -6,9 +6,10 @@
#include <memory>
#include <QWidget>
#include "common/common_types.h"
namespace Settings {
enum class StereoRenderOption;
enum class StereoRenderOption : u32;
}
namespace Ui {

View File

@@ -6,10 +6,11 @@
#include <QFileDialog>
#include <QMessageBox>
#include <QUrl>
#include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_general.h"
#include "citra_qt/uisettings.h"
#include "core/core.h"
#include "core/settings.h"
#include "common/file_util.h"
#include "common/settings.h"
#include "ui_configure_general.h"
// The QSlider doesn't have an easy way to set a custom step amount,
@@ -31,14 +32,17 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
// Set a minimum width for the label to prevent the slider from changing size.
// This scales across DPIs, and is acceptable for uncapitalized strings.
ui->emulation_speed_display_label->setMinimumWidth(tr("unthrottled").size() * 6);
ui->emulation_speed_combo->setVisible(!Settings::IsConfiguringGlobal());
ui->screenshot_combo->setVisible(!Settings::IsConfiguringGlobal());
SetupPerGameUI();
SetConfiguration();
ui->updateBox->setVisible(UISettings::values.updater_found);
connect(ui->button_reset_defaults, &QPushButton::clicked, this,
&ConfigureGeneral::ResetDefaults);
connect(ui->frame_limit, &QSlider::valueChanged, [&](int value) {
connect(ui->frame_limit, &QSlider::valueChanged, this, [&](int value) {
if (value == ui->frame_limit->maximum()) {
ui->emulation_speed_display_label->setText(tr("unthrottled"));
} else {
@@ -49,17 +53,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
}
});
connect(ui->frame_limit_alternate, &QSlider::valueChanged, [&](int value) {
if (value == ui->frame_limit_alternate->maximum()) {
ui->emulation_speed_alternate_display_label->setText(tr("unthrottled"));
} else {
ui->emulation_speed_alternate_display_label->setText(
QStringLiteral("%1%")
.arg(SliderToSettings(value))
.rightJustified(tr("unthrottled").size()));
}
});
connect(ui->change_screenshot_dir, &QToolButton::clicked, this, [this] {
const QString dir_path = QFileDialog::getExistingDirectory(
this, tr("Select Screenshot Directory"), ui->screenshot_dir_path->text(),
@@ -73,20 +66,21 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
ConfigureGeneral::~ConfigureGeneral() = default;
void ConfigureGeneral::SetConfiguration() {
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse);
if (Settings::IsConfiguringGlobal()) {
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue());
ui->toggle_background_pause->setChecked(
UISettings::values.pause_when_in_background.GetValue());
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
ui->toggle_update_check->setChecked(UISettings::values.check_for_update_on_start);
ui->toggle_auto_update->setChecked(UISettings::values.update_on_close);
ui->toggle_update_check->setChecked(
UISettings::values.check_for_update_on_start.GetValue());
ui->toggle_auto_update->setChecked(UISettings::values.update_on_close.GetValue());
}
// The first item is "auto-select" with actual value -1, so plus one here will do the trick
ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1);
if (Settings::values.frame_limit == 0) {
if (Settings::values.frame_limit.GetValue() == 0) {
ui->frame_limit->setValue(ui->frame_limit->maximum());
} else {
ui->frame_limit->setValue(SettingsToSlider(Settings::values.frame_limit));
ui->frame_limit->setValue(SettingsToSlider(Settings::values.frame_limit.GetValue()));
}
if (ui->frame_limit->value() == ui->frame_limit->maximum()) {
ui->emulation_speed_display_label->setText(tr("unthrottled"));
@@ -97,32 +91,48 @@ void ConfigureGeneral::SetConfiguration() {
.rightJustified(tr("unthrottled").size()));
}
ui->toggle_alternate_speed->setChecked(Settings::values.use_frame_limit_alternate);
if (Settings::values.frame_limit_alternate == 0) {
ui->frame_limit_alternate->setValue(ui->frame_limit_alternate->maximum());
if (!Settings::IsConfiguringGlobal()) {
if (Settings::values.frame_limit.UsingGlobal()) {
ui->emulation_speed_combo->setCurrentIndex(0);
ui->frame_limit->setEnabled(false);
} else {
ui->emulation_speed_combo->setCurrentIndex(1);
ui->frame_limit->setEnabled(true);
}
if (UISettings::values.screenshot_path.UsingGlobal()) {
ui->screenshot_combo->setCurrentIndex(0);
ui->screenshot_dir_path->setEnabled(false);
ui->change_screenshot_dir->setEnabled(false);
} else {
ui->screenshot_combo->setCurrentIndex(1);
ui->screenshot_dir_path->setEnabled(true);
ui->change_screenshot_dir->setEnabled(true);
}
ConfigurationShared::SetHighlight(ui->widget_screenshot,
!UISettings::values.screenshot_path.UsingGlobal());
ConfigurationShared::SetHighlight(ui->emulation_speed_layout,
!Settings::values.frame_limit.UsingGlobal());
ConfigurationShared::SetHighlight(ui->widget_region,
!Settings::values.region_value.UsingGlobal());
const bool is_region_global = Settings::values.region_value.UsingGlobal();
ui->region_combobox->setCurrentIndex(
is_region_global ? ConfigurationShared::USE_GLOBAL_INDEX
: static_cast<int>(Settings::values.region_value.GetValue()) +
ConfigurationShared::USE_GLOBAL_OFFSET + 1);
} else {
ui->frame_limit_alternate->setValue(
SettingsToSlider(Settings::values.frame_limit_alternate));
}
if (ui->frame_limit_alternate->value() == ui->frame_limit_alternate->maximum()) {
ui->emulation_speed_alternate_display_label->setText(tr("unthrottled"));
} else {
ui->emulation_speed_alternate_display_label->setText(
QStringLiteral("%1%")
.arg(SliderToSettings(ui->frame_limit_alternate->value()))
.rightJustified(tr("unthrottled").size()));
// The first item is "auto-select" with actual value -1, so plus one here will do the trick
ui->region_combobox->setCurrentIndex(Settings::values.region_value.GetValue() + 1);
}
QString screenshot_path = UISettings::values.screenshot_path;
if (screenshot_path.isEmpty()) {
screenshot_path =
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir));
screenshot_path.append(QStringLiteral("screenshots/"));
FileUtil::CreateFullPath(screenshot_path.toStdString());
UISettings::values.screenshot_path.SetGlobal(ui->screenshot_combo->currentIndex() ==
ConfigurationShared::USE_GLOBAL_INDEX);
std::string screenshot_path = UISettings::values.screenshot_path.GetValue();
if (screenshot_path.empty()) {
screenshot_path = FileUtil::GetUserPath(FileUtil::UserPath::UserDir) + "screenshots/";
FileUtil::CreateFullPath(screenshot_path);
UISettings::values.screenshot_path = screenshot_path;
}
ui->screenshot_dir_path->setText(screenshot_path);
ui->screenshot_dir_path->setText(QString::fromStdString(screenshot_path));
}
void ConfigureGeneral::ResetDefaults() {
@@ -139,31 +149,57 @@ void ConfigureGeneral::ResetDefaults() {
}
void ConfigureGeneral::ApplyConfiguration() {
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_value, ui->region_combobox,
[](s32 index) { return index - 1; });
UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked();
UISettings::values.update_on_close = ui->toggle_auto_update->isChecked();
ConfigurationShared::ApplyPerGameSetting(
&Settings::values.frame_limit, ui->emulation_speed_combo, [this](s32) {
const bool is_maximum = ui->frame_limit->value() == ui->frame_limit->maximum();
return is_maximum ? 0 : SliderToSettings(ui->frame_limit->value());
});
UISettings::values.screenshot_path = ui->screenshot_dir_path->text();
ConfigurationShared::ApplyPerGameSetting(
&UISettings::values.screenshot_path, ui->screenshot_combo,
[this](s32) { return ui->screenshot_dir_path->text().toStdString(); });
Settings::values.region_value = ui->region_combobox->currentIndex() - 1;
if (Settings::IsConfiguringGlobal()) {
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
if (ui->frame_limit->value() == ui->frame_limit->maximum()) {
Settings::values.frame_limit = 0;
} else {
Settings::values.frame_limit = SliderToSettings(ui->frame_limit->value());
}
Settings::values.use_frame_limit_alternate = ui->toggle_alternate_speed->isChecked();
if (ui->frame_limit_alternate->value() == ui->frame_limit_alternate->maximum()) {
Settings::values.frame_limit_alternate = 0;
} else {
Settings::values.frame_limit_alternate =
SliderToSettings(ui->frame_limit_alternate->value());
UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked();
UISettings::values.update_on_close = ui->toggle_auto_update->isChecked();
}
}
void ConfigureGeneral::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureGeneral::SetupPerGameUI() {
if (Settings::IsConfiguringGlobal()) {
ui->region_combobox->setEnabled(Settings::values.region_value.UsingGlobal());
ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
return;
}
connect(ui->emulation_speed_combo, qOverload<int>(&QComboBox::activated), this,
[this](int index) {
ui->frame_limit->setEnabled(index == 1);
ConfigurationShared::SetHighlight(ui->emulation_speed_layout, index == 1);
});
connect(ui->screenshot_combo, qOverload<int>(&QComboBox::activated), this, [this](int index) {
ui->screenshot_dir_path->setEnabled(index == 1);
ui->change_screenshot_dir->setEnabled(index == 1);
ConfigurationShared::SetHighlight(ui->widget_screenshot, index == 1);
});
ui->general_group->setVisible(false);
ui->updateBox->setVisible(false);
ui->button_reset_defaults->setVisible(false);
ConfigurationShared::SetColoredComboBox(
ui->region_combobox, ui->widget_region,
static_cast<u32>(Settings::values.region_value.GetValue(true) + 1));
}

View File

@@ -25,6 +25,8 @@ public:
void RetranslateUI();
void SetConfiguration();
void SetupPerGameUI();
private:
std::unique_ptr<Ui::ConfigureGeneral> ui;
};

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>408</width>
<height>436</height>
<width>524</width>
<height>578</height>
</rect>
</property>
<property name="windowTitle">
@@ -17,7 +17,7 @@
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<widget class="QGroupBox" name="general_group">
<property name="title">
<string>General</string>
</property>
@@ -70,144 +70,152 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<widget class="QGroupBox" name="emulation_group">
<property name="title">
<string>Emulation</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="QComboBox" name="region_combobox">
<item>
<property name="text">
<string>Auto-select</string>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QWidget" name="widget_region" native="true">
<layout class="QHBoxLayout" name="region_layout">
<property name="leftMargin">
<number>0</number>
</property>
</item>
<item>
<property name="text">
<string notr="true">JPN</string>
<property name="topMargin">
<number>0</number>
</property>
</item>
<item>
<property name="text">
<string notr="true">USA</string>
<property name="rightMargin">
<number>0</number>
</property>
</item>
<item>
<property name="text">
<string notr="true">EUR</string>
<property name="bottomMargin">
<number>0</number>
</property>
</item>
<item>
<property name="text">
<string notr="true">AUS</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">CHN</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">KOR</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">TWN</string>
</property>
</item>
<item>
<widget class="QLabel" name="region_label">
<property name="text">
<string>Region:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="region_combobox">
<item>
<property name="text">
<string>Auto-select</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">JPN</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">USA</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">EUR</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">AUS</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">CHN</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">KOR</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">TWN</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_emulation_speed">
<property name="text">
<string>Emulation Speed:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="toggle_alternate_speed">
<property name="text">
<string>Use Alternate Speed:</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QSlider" name="frame_limit">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>199</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="pageStep">
<number>15</number>
</property>
<property name="value">
<number>19</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="emulation_speed_display_label">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Region:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QSlider" name="frame_limit_alternate">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>199</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="pageStep">
<number>15</number>
</property>
<property name="value">
<number>39</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="emulation_speed_alternate_display_label">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item>
<widget class="QWidget" name="emulation_speed_layout" native="true">
<layout class="QHBoxLayout" name="emulation_speed_layout_inner">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="emulation_speed_combo">
<item>
<property name="text">
<string>Use global emulation speed</string>
</property>
</item>
<item>
<property name="text">
<string>Set emulation speed:</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_emulation_speed">
<property name="text">
<string>Emulation Speed:</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="frame_limit">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>199</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="pageStep">
<number>15</number>
</property>
<property name="value">
<number>19</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="emulation_speed_display_label">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
@@ -218,23 +226,57 @@
<property name="title">
<string>Screenshots</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Save Screenshots To</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="screenshot_dir_path">
</widget>
</item>
<item>
<widget class="QToolButton" name="change_screenshot_dir">
<property name="text">
<string>...</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="0" column="1">
<widget class="QWidget" name="widget_screenshot" native="true">
<layout class="QHBoxLayout" name="screenshot_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="screenshot_combo">
<item>
<property name="text">
<string>Use global screenshot path</string>
</property>
</item>
<item>
<property name="text">
<string>Set screenshot path:</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="screenshot_dir_label">
<property name="text">
<string>Save Screenshots To</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="screenshot_dir_path"/>
</item>
<item>
<widget class="QToolButton" name="change_screenshot_dir">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
@@ -270,12 +312,6 @@
<tabstop>toggle_hide_mouse</tabstop>
<tabstop>toggle_update_check</tabstop>
<tabstop>toggle_auto_update</tabstop>
<tabstop>region_combobox</tabstop>
<tabstop>frame_limit</tabstop>
<tabstop>toggle_alternate_speed</tabstop>
<tabstop>frame_limit_alternate</tabstop>
<tabstop>screenshot_dir_path</tabstop>
<tabstop>change_screenshot_dir</tabstop>
<tabstop>button_reset_defaults</tabstop>
</tabstops>
<resources/>

View File

@@ -6,22 +6,25 @@
#ifdef __APPLE__
#include <QMessageBox>
#endif
#include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_graphics.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_graphics.h"
#include "video_core/renderer_opengl/post_processing_opengl.h"
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) {
ui->setupUi(this);
SetupPerGameUI();
SetConfiguration();
ui->hw_renderer_group->setEnabled(ui->toggle_hw_renderer->isChecked());
ui->hw_renderer_group->setEnabled(ui->hw_renderer_group->isEnabled() &&
ui->toggle_hw_renderer->isChecked());
ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn());
connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] {
auto checked = ui->toggle_hw_renderer->isChecked();
const bool checked = ui->toggle_hw_renderer->isChecked();
ui->hw_renderer_group->setEnabled(checked);
ui->toggle_disk_shader_cache->setEnabled(checked && ui->toggle_hw_shader->isChecked());
});
@@ -31,7 +34,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
ui->toggle_hw_shader->isChecked());
connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] {
auto checked = ui->toggle_hw_shader->isChecked();
const bool checked = ui->toggle_hw_shader->isChecked();
ui->hw_shader_group->setEnabled(checked);
ui->toggle_disk_shader_cache->setEnabled(checked);
});
@@ -62,25 +65,67 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
ConfigureGraphics::~ConfigureGraphics() = default;
void ConfigureGraphics::SetConfiguration() {
ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer);
ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader);
ui->toggle_separable_shader->setChecked(Settings::values.separable_shader);
ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul);
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit);
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new);
ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer.GetValue());
ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue());
ui->toggle_separable_shader->setChecked(Settings::values.separable_shader.GetValue());
ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue());
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new.GetValue());
if (Settings::IsConfiguringGlobal()) {
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit.GetValue());
}
}
void ConfigureGraphics::ApplyConfiguration() {
Settings::values.use_hw_renderer = ui->toggle_hw_renderer->isChecked();
Settings::values.use_hw_shader = ui->toggle_hw_shader->isChecked();
Settings::values.separable_shader = ui->toggle_separable_shader->isChecked();
Settings::values.shaders_accurate_mul = ui->toggle_accurate_mul->isChecked();
Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked();
Settings::values.use_disk_shader_cache = ui->toggle_disk_shader_cache->isChecked();
Settings::values.use_vsync_new = ui->toggle_vsync_new->isChecked();
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_renderer,
ui->toggle_hw_renderer, use_hw_renderer);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_shader, ui->toggle_hw_shader,
use_hw_shader);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.separable_shader,
ui->toggle_separable_shader, separable_shader);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.shaders_accurate_mul,
ui->toggle_accurate_mul, shaders_accurate_mul);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
ui->toggle_disk_shader_cache, use_disk_shader_cache);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync_new, ui->toggle_vsync_new,
use_vsync_new);
if (Settings::IsConfiguringGlobal()) {
Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked();
}
}
void ConfigureGraphics::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureGraphics::SetupPerGameUI() {
// Block the global settings if a game is currently running that overrides them
if (Settings::IsConfiguringGlobal()) {
ui->toggle_hw_renderer->setEnabled(Settings::values.use_hw_renderer.UsingGlobal());
ui->toggle_hw_shader->setEnabled(Settings::values.use_hw_shader.UsingGlobal());
ui->toggle_separable_shader->setEnabled(Settings::values.separable_shader.UsingGlobal());
ui->toggle_accurate_mul->setEnabled(Settings::values.shaders_accurate_mul.UsingGlobal());
ui->toggle_disk_shader_cache->setEnabled(
Settings::values.use_disk_shader_cache.UsingGlobal());
ui->toggle_vsync_new->setEnabled(Settings::values.use_vsync_new.UsingGlobal());
return;
}
ui->toggle_shader_jit->setVisible(false);
ConfigurationShared::SetColoredTristate(ui->toggle_hw_renderer,
Settings::values.use_hw_renderer, use_hw_renderer);
ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader,
use_hw_shader);
ConfigurationShared::SetColoredTristate(ui->toggle_separable_shader,
Settings::values.separable_shader, separable_shader);
ConfigurationShared::SetColoredTristate(
ui->toggle_accurate_mul, Settings::values.shaders_accurate_mul, shaders_accurate_mul);
ConfigurationShared::SetColoredTristate(ui->toggle_disk_shader_cache,
Settings::values.use_disk_shader_cache,
use_disk_shader_cache);
ConfigurationShared::SetColoredTristate(ui->toggle_vsync_new, Settings::values.use_vsync_new,
use_vsync_new);
}

View File

@@ -11,6 +11,10 @@ namespace Ui {
class ConfigureGraphics;
}
namespace ConfigurationShared {
enum class CheckState;
}
class ConfigureGraphics : public QWidget {
Q_OBJECT
@@ -24,6 +28,14 @@ public:
void UpdateBackgroundColorButton(const QColor& color);
void SetupPerGameUI();
ConfigurationShared::CheckState use_hw_renderer;
ConfigurationShared::CheckState use_hw_shader;
ConfigurationShared::CheckState separable_shader;
ConfigurationShared::CheckState shaders_accurate_mul;
ConfigurationShared::CheckState use_disk_shader_cache;
ConfigurationShared::CheckState use_vsync_new;
std::unique_ptr<Ui::ConfigureGraphics> ui;
QColor bg_color;
};

View File

@@ -7,7 +7,7 @@
#include "citra_qt/configuration/configure_hotkeys.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/util/sequence_dialog/sequence_dialog.h"
#include "core/settings.h"
#include "common/settings.h"
#include "ui_configure_hotkeys.h"
ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)

View File

@@ -13,7 +13,7 @@
#include <QKeySequence>
#include <QWidget>
#include "common/param_package.h"
#include "core/settings.h"
#include "common/settings.h"
#include "input_common/main.h"
class QKeyEvent;

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <QDialog>
#include "common/param_package.h"
#include "core/settings.h"
#include "common/settings.h"
#include "input_common/main.h"
#include "input_common/udp/udp.h"

View File

@@ -0,0 +1,128 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <utility>
#include <vector>
#include <QPushButton>
#include <QString>
#include <fmt/format.h>
#include "citra_qt/configuration/config.h"
#include "citra_qt/configuration/configure_audio.h"
#include "citra_qt/configuration/configure_general.h"
#include "citra_qt/configuration/configure_graphics.h"
#include "citra_qt/configuration/configure_per_game.h"
#include "citra_qt/configuration/configure_system.h"
#include "citra_qt/util/util.h"
#include "core/core.h"
#include "core/loader/loader.h"
#include "core/loader/smdh.h"
#include "ui_configure_per_game.h"
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
Core::System& system_)
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()),
filename{file_name.toStdString()}, title_id{title_id_}, system{system_} {
const auto config_file_name = title_id == 0 ? filename : fmt::format("{:016X}", title_id);
game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
audio_tab = std::make_unique<ConfigureAudio>(this);
general_tab = std::make_unique<ConfigureGeneral>(this);
graphics_tab = std::make_unique<ConfigureGraphics>(this);
system_tab = std::make_unique<ConfigureSystem>(this);
ui->setupUi(this);
ui->tabWidget->addTab(general_tab.get(), tr("General"));
ui->tabWidget->addTab(system_tab.get(), tr("System"));
ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
setFocusPolicy(Qt::ClickFocus);
setWindowTitle(tr("Properties"));
// remove Help question mark button from the title bar
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
scene = new QGraphicsScene;
ui->icon_view->setScene(scene);
if (system.IsPoweredOn()) {
QPushButton* apply_button = ui->buttonBox->addButton(QDialogButtonBox::Apply);
connect(apply_button, &QAbstractButton::clicked, this,
&ConfigurePerGame::HandleApplyButtonClicked);
}
LoadConfiguration();
}
ConfigurePerGame::~ConfigurePerGame() = default;
void ConfigurePerGame::ApplyConfiguration() {
general_tab->ApplyConfiguration();
system_tab->ApplyConfiguration();
graphics_tab->ApplyConfiguration();
audio_tab->ApplyConfiguration();
Settings::LogSettings();
game_config->Save();
}
void ConfigurePerGame::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QDialog::changeEvent(event);
}
void ConfigurePerGame::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigurePerGame::HandleApplyButtonClicked() {
ApplyConfiguration();
}
static QPixmap GetQPixmapFromSMDH(std::vector<u8>& smdh_data) {
Loader::SMDH smdh;
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
bool large = true;
std::vector<u16> icon_data = smdh.GetIcon(large);
const uchar* data = reinterpret_cast<const uchar*>(icon_data.data());
int size = large ? 48 : 24;
QImage icon(data, size, size, QImage::Format::Format_RGB16);
return QPixmap::fromImage(icon);
}
void ConfigurePerGame::LoadConfiguration() {
if (filename.empty()) {
return;
}
ui->display_title_id->setText(
QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
const auto loader = Loader::GetLoader(filename);
std::string title;
if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
ui->display_name->setText(QString::fromStdString(title));
std::vector<u8> bytes;
if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
scene->clear();
QPixmap map = GetQPixmapFromSMDH(bytes);
scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
ui->display_filepath->setText(QString::fromStdString(filename));
ui->display_format->setText(
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
const auto valueText = ReadableByteSize(FileUtil::GetSize(filename));
ui->display_size->setText(valueText);
}

View File

@@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <string>
#include <QDialog>
#include <QList>
#include "citra_qt/configuration/config.h"
namespace Core {
class System;
}
class ConfigureAudio;
class ConfigureGeneral;
class ConfigureGraphics;
class ConfigureSystem;
class QGraphicsScene;
class QStandardItem;
class QStandardItemModel;
class QTreeView;
class QVBoxLayout;
namespace Ui {
class ConfigurePerGame;
}
class ConfigurePerGame : public QDialog {
Q_OBJECT
public:
// Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263
explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
Core::System& system_);
~ConfigurePerGame() override;
/// Loads all button configurations to settings file
void LoadConfiguration();
/// Save all button configurations to settings file
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void HandleApplyButtonClicked();
std::unique_ptr<Ui::ConfigurePerGame> ui;
std::string filename;
u64 title_id;
QGraphicsScene* scene;
std::unique_ptr<Config> game_config;
Core::System& system;
std::unique_ptr<ConfigureAudio> audio_tab;
std::unique_ptr<ConfigureGeneral> general_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureSystem> system_tab;
};

View File

@@ -0,0 +1,264 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigurePerGame</class>
<widget class="QDialog" name="ConfigurePerGame">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>900</width>
<height>661</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>900</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Info</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGraphicsView" name="icon_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="interactive">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="4" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="display_filepath">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>160</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Format</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="display_size">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Filepath</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Title ID</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="display_title_id">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="display_name">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="display_format">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="VerticalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="currentIndex">
<number>-1</number>
</property>
<property name="usesScrollButtons">
<bool>true</bool>
</property>
<property name="documentMode">
<bool>false</bool>
</property>
<property name="tabsClosable">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ConfigurePerGame</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigurePerGame</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -6,8 +6,8 @@
#include <QFileDialog>
#include <QUrl>
#include "citra_qt/configuration/configure_storage.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_storage.h"
ConfigureStorage::ConfigureStorage(QWidget* parent)
@@ -60,7 +60,7 @@ ConfigureStorage::ConfigureStorage(QWidget* parent)
ConfigureStorage::~ConfigureStorage() = default;
void ConfigureStorage::SetConfiguration() {
ui->nand_group->setVisible(Settings::values.use_custom_storage);
ui->nand_group->setVisible(Settings::values.use_custom_storage.GetValue());
QString nand_path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
ui->nand_dir_path->setText(nand_path);
ui->open_nand_dir->setEnabled(!nand_path.isEmpty());
@@ -71,8 +71,8 @@ void ConfigureStorage::SetConfiguration() {
ui->sdmc_dir_path->setText(sdmc_path);
ui->open_sdmc_dir->setEnabled(!sdmc_path.isEmpty());
ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd);
ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage);
ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd.GetValue());
ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage.GetValue());
ui->storage_group->setEnabled(!Core::System::GetInstance().IsPoweredOn());
}

View File

@@ -4,12 +4,12 @@
#include <cstring>
#include <QMessageBox>
#include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_system.h"
#include "citra_qt/uisettings.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/ptm/ptm.h"
#include "core/settings.h"
#include "ui_configure_system.h"
static const std::array<int, 12> days_in_month = {{
@@ -249,10 +249,14 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
// This scales across DPIs. (This value should be enough for "xxx%")
ui->clock_display_label->setMinimumWidth(40);
connect(ui->slider_clock_speed, &QSlider::valueChanged, [&](int value) {
connect(ui->slider_clock_speed, &QSlider::valueChanged, this, [&](int value) {
ui->clock_display_label->setText(QStringLiteral("%1%").arg(SliderToSettings(value)));
});
ui->clock_speed_label->setVisible(Settings::IsConfiguringGlobal());
ui->clock_speed_combo->setVisible(!Settings::IsConfiguringGlobal());
SetupPerGameUI();
ConfigureTime();
}
@@ -261,12 +265,12 @@ ConfigureSystem::~ConfigureSystem() = default;
void ConfigureSystem::SetConfiguration() {
enabled = !Core::System::GetInstance().IsPoweredOn();
ui->combo_init_clock->setCurrentIndex(static_cast<u8>(Settings::values.init_clock));
ui->combo_init_clock->setCurrentIndex(static_cast<u8>(Settings::values.init_clock.GetValue()));
QDateTime date_time;
date_time.setTime_t(Settings::values.init_time);
date_time.setTime_t(Settings::values.init_time.GetValue());
ui->edit_init_time->setDateTime(date_time);
long long init_time_offset = Settings::values.init_time_offset;
long long init_time_offset = Settings::values.init_time_offset.GetValue();
long long days_offset = init_time_offset / 86400;
ui->edit_init_time_offset_days->setValue(days_offset);
@@ -287,20 +291,29 @@ void ConfigureSystem::SetConfiguration() {
ui->label_disable_info->hide();
}
ui->slider_clock_speed->setValue(SettingsToSlider(Settings::values.cpu_clock_percentage));
ui->clock_display_label->setText(
QStringLiteral("%1%").arg(Settings::values.cpu_clock_percentage));
if (!Settings::IsConfiguringGlobal()) {
if (Settings::values.cpu_clock_percentage.UsingGlobal()) {
ui->clock_speed_combo->setCurrentIndex(0);
ui->slider_clock_speed->setEnabled(false);
} else {
ui->clock_speed_combo->setCurrentIndex(1);
ui->slider_clock_speed->setEnabled(true);
}
ConfigurationShared::SetHighlight(ui->clock_speed_widget,
!Settings::values.cpu_clock_percentage.UsingGlobal());
}
ui->toggle_new_3ds->setChecked(Settings::values.is_new_3ds);
ui->slider_clock_speed->setValue(
SettingsToSlider(Settings::values.cpu_clock_percentage.GetValue()));
ui->clock_display_label->setText(
QStringLiteral("%1%").arg(Settings::values.cpu_clock_percentage.GetValue()));
ui->toggle_new_3ds->setChecked(Settings::values.is_new_3ds.GetValue());
}
void ConfigureSystem::ReadSystemSettings() {
// set username
username = cfg->GetUsername();
// TODO(wwylele): Use this when we move to Qt 5.5
// ui->edit_username->setText(QString::fromStdU16String(username));
ui->edit_username->setText(
QString::fromUtf16(reinterpret_cast<const ushort*>(username.data())));
ui->edit_username->setText(QString::fromStdU16String(username));
// set birthday
std::tie(birthmonth, birthday) = cfg->GetBirthday();
@@ -337,32 +350,29 @@ void ConfigureSystem::ApplyConfiguration() {
bool modified = false;
// apply username
// TODO(wwylele): Use this when we move to Qt 5.5
// std::u16string new_username = ui->edit_username->text().toStdU16String();
std::u16string new_username(
reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16()));
std::u16string new_username = ui->edit_username->text().toStdU16String();
if (new_username != username) {
cfg->SetUsername(new_username);
modified = true;
}
// apply birthday
int new_birthmonth = ui->combo_birthmonth->currentIndex() + 1;
int new_birthday = ui->combo_birthday->currentIndex() + 1;
s32 new_birthmonth = ui->combo_birthmonth->currentIndex() + 1;
s32 new_birthday = ui->combo_birthday->currentIndex() + 1;
if (birthmonth != new_birthmonth || birthday != new_birthday) {
cfg->SetBirthday(new_birthmonth, new_birthday);
modified = true;
}
// apply language
int new_language = ui->combo_language->currentIndex();
s32 new_language = ui->combo_language->currentIndex();
if (language_index != new_language) {
cfg->SetSystemLanguage(static_cast<Service::CFG::SystemLanguage>(new_language));
modified = true;
}
// apply sound
int new_sound = ui->combo_sound->currentIndex();
s32 new_sound = ui->combo_sound->currentIndex();
if (sound_index != new_sound) {
cfg->SetSoundOutputMode(static_cast<Service::CFG::SoundOutputMode>(new_sound));
modified = true;
@@ -386,6 +396,9 @@ void ConfigureSystem::ApplyConfiguration() {
cfg->UpdateConfigNANDSavegame();
}
ConfigurationShared::ApplyPerGameSetting(&Settings::values.is_new_3ds, ui->toggle_new_3ds,
is_new_3ds);
Settings::values.init_clock =
static_cast<Settings::InitClock>(ui->combo_init_clock->currentIndex());
Settings::values.init_time = ui->edit_init_time->dateTime().toTime_t();
@@ -398,11 +411,11 @@ void ConfigureSystem::ApplyConfiguration() {
}
Settings::values.init_time_offset = time_offset_days + time_offset_time;
Settings::values.is_new_3ds = ui->toggle_new_3ds->isChecked();
}
Settings::values.cpu_clock_percentage = SliderToSettings(ui->slider_clock_speed->value());
Settings::Apply();
ConfigurationShared::ApplyPerGameSetting(
&Settings::values.cpu_clock_percentage, ui->clock_speed_combo,
[this](s32) { return SliderToSettings(ui->slider_clock_speed->value()); });
}
void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
@@ -410,10 +423,10 @@ void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
return;
// store current day selection
int birthday_index = ui->combo_birthday->currentIndex();
s32 birthday_index = ui->combo_birthday->currentIndex();
// get number of days in the new selected month
int days = days_in_month[birthmonth_index];
s32 days = days_in_month[birthmonth_index];
// if the selected day is out of range,
// reset it to 1st
@@ -422,7 +435,7 @@ void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
// update the day combo box
ui->combo_birthday->clear();
for (int i = 1; i <= days; ++i) {
for (s32 i = 1; i <= days; ++i) {
ui->combo_birthday->addItem(QString::number(i));
}
@@ -442,15 +455,16 @@ void ConfigureSystem::ConfigureTime() {
}
void ConfigureSystem::UpdateInitTime(int init_clock) {
const bool is_global = Settings::IsConfiguringGlobal();
const bool is_fixed_time =
static_cast<Settings::InitClock>(init_clock) == Settings::InitClock::FixedTime;
ui->label_init_time->setVisible(is_fixed_time);
ui->edit_init_time->setVisible(is_fixed_time);
ui->label_init_time->setVisible(is_fixed_time && is_global);
ui->edit_init_time->setVisible(is_fixed_time && is_global);
ui->label_init_time_offset->setVisible(!is_fixed_time);
ui->edit_init_time_offset_days->setVisible(!is_fixed_time);
ui->edit_init_time_offset_time->setVisible(!is_fixed_time);
ui->label_init_time_offset->setVisible(!is_fixed_time && is_global);
ui->edit_init_time_offset_days->setVisible(!is_fixed_time && is_global);
ui->edit_init_time_offset_time->setVisible(!is_fixed_time && is_global);
}
void ConfigureSystem::RefreshConsoleID() {
@@ -475,3 +489,43 @@ void ConfigureSystem::RefreshConsoleID() {
void ConfigureSystem::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureSystem::SetupPerGameUI() {
// Block the global settings if a game is currently running that overrides them
if (Settings::IsConfiguringGlobal()) {
ui->toggle_new_3ds->setEnabled(Settings::values.is_new_3ds.UsingGlobal());
ui->slider_clock_speed->setEnabled(Settings::values.cpu_clock_percentage.UsingGlobal());
return;
}
// Hide most settings for now, we can implement them later
ui->label_username->setVisible(false);
ui->label_birthday->setVisible(false);
ui->label_init_clock->setVisible(false);
ui->label_init_time->setVisible(false);
ui->label_console_id->setVisible(false);
ui->label_sound->setVisible(false);
ui->label_language->setVisible(false);
ui->label_country->setVisible(false);
ui->label_play_coins->setVisible(false);
ui->edit_username->setVisible(false);
ui->spinBox_play_coins->setVisible(false);
ui->combo_birthday->setVisible(false);
ui->combo_birthmonth->setVisible(false);
ui->combo_init_clock->setVisible(false);
ui->combo_sound->setVisible(false);
ui->combo_language->setVisible(false);
ui->combo_country->setVisible(false);
ui->label_init_time_offset->setVisible(false);
ui->edit_init_time_offset_days->setVisible(false);
ui->edit_init_time_offset_time->setVisible(false);
ui->button_regenerate_console_id->setVisible(false);
connect(ui->clock_speed_combo, qOverload<int>(&QComboBox::activated), this, [this](int index) {
ui->slider_clock_speed->setEnabled(index == 1);
ConfigurationShared::SetHighlight(ui->clock_speed_widget, index == 1);
});
ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds,
is_new_3ds);
}

View File

@@ -12,6 +12,10 @@ namespace Ui {
class ConfigureSystem;
}
namespace ConfigurationShared {
enum class CheckState;
}
namespace Service {
namespace CFG {
class Module;
@@ -37,6 +41,9 @@ private:
void UpdateInitTime(int init_clock);
void RefreshConsoleID();
void SetupPerGameUI();
ConfigurationShared::CheckState is_new_3ds;
std::unique_ptr<Ui::ConfigureSystem> ui;
bool enabled = false;

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>520</width>
<height>564</height>
<width>525</width>
<height>619</height>
</rect>
</property>
<property name="windowTitle">
@@ -344,54 +344,77 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<widget class="QGroupBox" name="group_advanced">
<property name="title">
<string>Advanced</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>CPU Clock Speed</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QSlider" name="slider_clock_speed">
<property name="toolTip">
<string>&lt;html&gt;&lt;body&gt;Changes the emulated CPU clock frequency.&lt;br&gt;Underclocking can increase performance but may cause the game to freeze.&lt;br&gt;Overclocking may reduce in game lag but also might cause freezes&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>79</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="pageStep">
<number>15</number>
</property>
<property name="value">
<number>25</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="clock_display_label">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<widget class="QWidget" name="clock_speed_widget" native="true">
<layout class="QHBoxLayout" name="clock_speed_layout">
<property name="spacing">
<number>7</number>
</property>
<item>
<widget class="QComboBox" name="clock_speed_combo">
<item>
<property name="text">
<string>Use global clock speed</string>
</property>
</item>
<item>
<property name="text">
<string>Set clock speed:</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="clock_speed_label">
<property name="text">
<string>CPU Clock Speed</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slider_clock_speed">
<property name="toolTip">
<string>&lt;html&gt;&lt;body&gt;Changes the emulated CPU clock frequency.&lt;br&gt;Underclocking can increase performance but may cause the game to freeze.&lt;br&gt;Overclocking may reduce in game lag but also might cause freezes&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>79</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="pageStep">
<number>15</number>
</property>
<property name="value">
<number>25</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="clock_display_label">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
@@ -449,7 +472,6 @@
<tabstop>edit_init_time</tabstop>
<tabstop>spinBox_play_coins</tabstop>
<tabstop>button_regenerate_console_id</tabstop>
<tabstop>slider_clock_speed</tabstop>
</tabstops>
<resources/>
<connections/>

View File

@@ -9,7 +9,7 @@
#include <optional>
#include <vector>
#include <QDialog>
#include "core/settings.h"
#include "common/settings.h"
class QItemSelection;
class QModelIndex;

View File

@@ -46,12 +46,14 @@ void ConfigureUi::SetConfiguration() {
ui->language_combobox->setCurrentIndex(
ui->language_combobox->findData(UISettings::values.language));
ui->icon_size_combobox->setCurrentIndex(
static_cast<int>(UISettings::values.game_list_icon_size));
ui->row_1_text_combobox->setCurrentIndex(static_cast<int>(UISettings::values.game_list_row_1));
ui->row_2_text_combobox->setCurrentIndex(static_cast<int>(UISettings::values.game_list_row_2) +
1);
ui->toggle_hide_no_icon->setChecked(UISettings::values.game_list_hide_no_icon);
ui->toggle_single_line_mode->setChecked(UISettings::values.game_list_single_line_mode);
static_cast<int>(UISettings::values.game_list_icon_size.GetValue()));
ui->row_1_text_combobox->setCurrentIndex(
static_cast<int>(UISettings::values.game_list_row_1.GetValue()));
ui->row_2_text_combobox->setCurrentIndex(
static_cast<int>(UISettings::values.game_list_row_2.GetValue()) + 1);
ui->toggle_hide_no_icon->setChecked(UISettings::values.game_list_hide_no_icon.GetValue());
ui->toggle_single_line_mode->setChecked(
UISettings::values.game_list_single_line_mode.GetValue());
}
void ConfigureUi::ApplyConfiguration() {

View File

@@ -87,7 +87,7 @@ void ConfigureWeb::SetConfiguration() {
tr("Telemetry ID: 0x%1").arg(QString::number(Core::GetTelemetryId(), 16).toUpper()));
user_verified = true;
ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence);
ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence.GetValue());
}
void ConfigureWeb::ApplyConfiguration() {

View File

@@ -15,10 +15,10 @@
namespace Debugger {
void ToggleConsole() {
static bool console_shown = false;
if (console_shown == UISettings::values.show_console) {
if (console_shown == UISettings::values.show_console.GetValue()) {
return;
} else {
console_shown = UISettings::values.show_console;
console_shown = UISettings::values.show_console.GetValue();
}
#ifdef _WIN32

View File

@@ -7,7 +7,7 @@
#include <QLayout>
#include <QScrollArea>
#include "citra_qt/debugger/lle_service_modules.h"
#include "core/settings.h"
#include "common/settings.h"
LLEServiceModulesWidget::LLEServiceModulesWidget(QWidget* parent)
: QDockWidget(tr("Toggle LLE Service Modules"), parent) {

View File

@@ -7,13 +7,13 @@
#include "citra_qt/uisettings.h"
#include "citra_qt/util/util.h"
#include "common/assert.h"
#include "common/settings.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
#include "core/hle/kernel/wait_object.h"
#include "core/settings.h"
namespace {

View File

@@ -7,7 +7,7 @@
#include "citra_qt/dumping/dumping_dialog.h"
#include "citra_qt/dumping/options_dialog.h"
#include "citra_qt/uisettings.h"
#include "core/settings.h"
#include "common/settings.h"
#include "ui_dumping_dialog.h"
DumpingDialog::DumpingDialog(QWidget* parent)

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <QApplication>
#include <QDir>
#include <QFileInfo>
#include <QFileSystemWatcher>
#include <QHBoxLayout>
@@ -28,9 +29,11 @@
#include "citra_qt/main.h"
#include "citra_qt/uisettings.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/archive_source_sd_savedata.h"
#include "core/hle/service/fs/archive.h"
#include "qcursor.h"
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent)
: QObject(parent), gamelist{gamelist} {}
@@ -462,9 +465,20 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
default:
break;
}
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
}
void ForEachOpenGLCacheFile(u64 program_id, auto func) {
for (const std::string_view cache_type : {"separable", "conventional"}) {
const std::string path = fmt::format("{}opengl/precompiled/{}/{:016X}.bin",
FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir),
cache_type, program_id);
QFile file{QString::fromStdString(path)};
func(file);
}
}
void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 program_id,
u64 extdata_id) {
QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
@@ -475,19 +489,32 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
QAction* open_texture_load_location =
context_menu.addAction(tr("Open Custom Texture Location"));
QAction* open_mods_location = context_menu.addAction(tr("Open Mods Location"));
QAction* open_dlc_location = context_menu.addAction(tr("Open DLC Data Location"));
QMenu* shader_menu = context_menu.addMenu(tr("Disk Shader Cache"));
QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
context_menu.addSeparator();
QAction* properties = context_menu.addAction(tr("Properties"));
QAction* open_shader_cache_location = shader_menu->addAction(tr("Open Shader Cache Location"));
shader_menu->addSeparator();
QAction* delete_opengl_disk_shader_cache =
shader_menu->addAction(tr("Delete OpenGL Shader Cache"));
const bool is_application =
0x0004000000000000 <= program_id && program_id <= 0x00040000FFFFFFFF;
bool opengl_cache_exists = false;
ForEachOpenGLCacheFile(
program_id, [&opengl_cache_exists](QFile& file) { opengl_cache_exists |= file.exists(); });
std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
open_save_location->setVisible(
open_save_location->setEnabled(
is_application && FileUtil::Exists(FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(
sdmc_dir, program_id)));
if (extdata_id) {
open_extdata_location->setVisible(
open_extdata_location->setEnabled(
is_application &&
FileUtil::Exists(FileSys::GetExtDataPathFromId(sdmc_dir, extdata_id)));
} else {
@@ -495,18 +522,20 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
}
auto media_type = Service::AM::GetTitleMediaType(program_id);
open_application_location->setVisible(path.toStdString() ==
open_application_location->setEnabled(path.toStdString() ==
Service::AM::GetTitleContentPath(media_type, program_id));
open_update_location->setVisible(
open_update_location->setEnabled(
is_application && FileUtil::Exists(Service::AM::GetTitlePath(Service::FS::MediaType::SDMC,
program_id + 0xe00000000) +
"content/"));
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
open_texture_dump_location->setVisible(is_application);
open_texture_load_location->setVisible(is_application);
open_mods_location->setVisible(is_application);
dump_romfs->setVisible(is_application);
open_texture_dump_location->setEnabled(is_application);
open_texture_load_location->setEnabled(is_application);
open_mods_location->setEnabled(is_application);
open_dlc_location->setEnabled(is_application);
dump_romfs->setEnabled(is_application);
delete_opengl_disk_shader_cache->setEnabled(opengl_cache_exists);
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end());
@@ -543,11 +572,32 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
emit OpenFolderRequested(program_id, GameListOpenTarget::MODS);
}
});
connect(open_dlc_location, &QAction::triggered, this, [this, program_id] {
const u64 trimmed_id = program_id & 0xFFFFFFF;
const std::string dlc_path =
fmt::format("{}Nintendo 3DS/00000000000000000000000000000000/"
"00000000000000000000000000000000/title/0004008c/{:08x}/content/",
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), trimmed_id);
fmt::print("DLC path {}\n", dlc_path);
if (FileUtil::CreateFullPath(dlc_path)) {
emit OpenFolderRequested(trimmed_id, GameListOpenTarget::DLC_DATA);
}
});
connect(dump_romfs, &QAction::triggered, this,
[this, path, program_id] { emit DumpRomFSRequested(path, program_id); });
connect(navigate_to_gamedb_entry, &QAction::triggered, this, [this, program_id]() {
emit NavigateToGamedbEntryRequested(program_id, compatibility_list);
});
connect(properties, &QAction::triggered, this,
[this, path]() { emit OpenPerGameGeneralRequested(path); });
connect(open_shader_cache_location, &QAction::triggered, this, [this, program_id] {
if (FileUtil::CreateFullPath(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir))) {
emit OpenFolderRequested(program_id, GameListOpenTarget::SHADER_CACHE);
}
});
connect(delete_opengl_disk_shader_cache, &QAction::triggered, this, [program_id] {
ForEachOpenGLCacheFile(program_id, [](QFile& file) { file.remove(); });
});
};
void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) {

View File

@@ -37,6 +37,8 @@ enum class GameListOpenTarget {
TEXTURE_DUMP = 4,
TEXTURE_LOAD = 5,
MODS = 6,
DLC_DATA = 7,
SHADER_CACHE = 8
};
class GameList : public QWidget {
@@ -82,6 +84,7 @@ signals:
void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
void NavigateToGamedbEntryRequested(u64 program_id,
const CompatibilityList& compatibility_list);
void OpenPerGameGeneralRequested(const QString file);
void DumpRomFSRequested(QString game_path, u64 program_id);
void OpenDirectory(const QString& directory);
void AddDirectory();

View File

@@ -160,17 +160,19 @@ public:
setData(qulonglong(program_id), ProgramIdRole);
setData(qulonglong(extdata_id), ExtdataIdRole);
if (UISettings::values.game_list_icon_size == UISettings::GameListIconSize::NoIcon) {
if (UISettings::values.game_list_icon_size.GetValue() ==
UISettings::GameListIconSize::NoIcon) {
// Do not display icons
setData(QPixmap(), Qt::DecorationRole);
}
bool large =
UISettings::values.game_list_icon_size == UISettings::GameListIconSize::LargeIcon;
bool large = UISettings::values.game_list_icon_size.GetValue() ==
UISettings::GameListIconSize::LargeIcon;
if (!Loader::IsValidSMDH(smdh_data)) {
// SMDH is not valid, set a default icon
if (UISettings::values.game_list_icon_size != UISettings::GameListIconSize::NoIcon)
if (UISettings::values.game_list_icon_size.GetValue() !=
UISettings::GameListIconSize::NoIcon)
setData(GetDefaultIcon(large), Qt::DecorationRole);
return;
}
@@ -179,7 +181,8 @@ public:
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
// Get icon from SMDH
if (UISettings::values.game_list_icon_size != UISettings::GameListIconSize::NoIcon) {
if (UISettings::values.game_list_icon_size.GetValue() !=
UISettings::GameListIconSize::NoIcon) {
setData(GetQPixmapFromSMDH(smdh, large), Qt::DecorationRole);
}
@@ -211,16 +214,17 @@ public:
QString::fromStdString(fmt::format("{:016X}", data(ProgramIdRole).toULongLong()))},
};
const QString& row1 = display_texts.at(UISettings::values.game_list_row_1).simplified();
const QString& row1 =
display_texts.at(UISettings::values.game_list_row_1.GetValue()).simplified();
if (role == SortRole)
return row1.toLower();
QString row2;
auto row_2_id = UISettings::values.game_list_row_2;
const auto row_2_id = UISettings::values.game_list_row_2.GetValue();
if (row_2_id != UISettings::GameListText::NoText) {
if (!row1.isEmpty()) {
row2 = UISettings::values.game_list_single_line_mode
row2 = UISettings::values.game_list_single_line_mode.GetValue()
? QStringLiteral(" ")
: QStringLiteral("\n ");
}
@@ -355,7 +359,7 @@ public:
UISettings::GameDir* game_dir = &directory;
setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole);
const int icon_size = IconSizes.at(UISettings::values.game_list_icon_size);
const int icon_size = IconSizes.at(UISettings::values.game_list_icon_size.GetValue());
switch (dir_type) {
case GameListItemType::InstalledDir:
setData(QIcon::fromTheme(QStringLiteral("sd_card")).pixmap(icon_size),
@@ -398,7 +402,7 @@ public:
explicit GameListAddDir() {
setData(type(), TypeRole);
int icon_size = IconSizes.at(UISettings::values.game_list_icon_size);
int icon_size = IconSizes.at(UISettings::values.game_list_icon_size.GetValue());
setData(QIcon::fromTheme(QStringLiteral("plus")).pixmap(icon_size), Qt::DecorationRole);
setData(QObject::tr("Add New Game Directory"), Qt::DisplayRole);
}

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <clocale>
#include <filesystem>
#include <fstream>
#include <memory>
#include <thread>
@@ -34,6 +35,7 @@
#include "citra_qt/compatibility_list.h"
#include "citra_qt/configuration/config.h"
#include "citra_qt/configuration/configure_dialog.h"
#include "citra_qt/configuration/configure_per_game.h"
#include "citra_qt/debugger/console.h"
#include "citra_qt/debugger/graphics/graphics.h"
#include "citra_qt/debugger/graphics/graphics_breakpoints.h"
@@ -70,9 +72,11 @@
#include "common/microprofile.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
#include "common/string_util.h"
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#endif
#include "common/settings.h"
#include "core/core.h"
#include "core/dumping/backend.h"
#include "core/file_sys/archive_extsavedata.h"
@@ -85,7 +89,6 @@
#include "core/loader/loader.h"
#include "core/movie.h"
#include "core/savestate.h"
#include "core/settings.h"
#include "game_list_p.h"
#include "input_common/main.h"
#include "network/network_settings.h"
@@ -124,11 +127,13 @@ enum class CalloutFlag : uint32_t {
};
void GMainWindow::ShowTelemetryCallout() {
if (UISettings::values.callout_flags & static_cast<uint32_t>(CalloutFlag::Telemetry)) {
if (UISettings::values.callout_flags.GetValue() &
static_cast<uint32_t>(CalloutFlag::Telemetry)) {
return;
}
UISettings::values.callout_flags |= static_cast<uint32_t>(CalloutFlag::Telemetry);
UISettings::values.callout_flags =
UISettings::values.callout_flags.GetValue() | static_cast<uint32_t>(CalloutFlag::Telemetry);
const QString telemetry_message =
tr("<a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Anonymous "
"data is collected</a> to help improve Citra. "
@@ -143,7 +148,7 @@ const int GMainWindow::max_recent_files_item;
static void InitializeLogging() {
Log::Filter log_filter;
log_filter.ParseFilterString(Settings::values.log_filter);
log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
Log::SetGlobalFilter(log_filter);
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
@@ -175,7 +180,7 @@ GMainWindow::GMainWindow()
default_theme_paths = QIcon::themeSearchPaths();
UpdateUITheme();
SetDiscordEnabled(UISettings::values.enable_discord_presence);
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
discord_rpc->Update();
Network::Init();
@@ -556,8 +561,7 @@ void GMainWindow::InitializeHotkeys() {
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Alternate Speed"), this),
&QShortcut::activated, this, [&] {
Settings::values.use_frame_limit_alternate =
!Settings::values.use_frame_limit_alternate;
Settings::values.frame_limit.SetGlobal(!Settings::values.frame_limit.UsingGlobal());
UpdateStatusBar();
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Texture Dumping"), this),
@@ -568,42 +572,25 @@ void GMainWindow::InitializeHotkeys() {
static constexpr u16 SPEED_LIMIT_STEP = 5;
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this),
&QShortcut::activated, this, [&] {
if (Settings::values.use_frame_limit_alternate) {
if (Settings::values.frame_limit_alternate == 0) {
return;
}
if (Settings::values.frame_limit_alternate < 995 - SPEED_LIMIT_STEP) {
Settings::values.frame_limit_alternate += SPEED_LIMIT_STEP;
} else {
Settings::values.frame_limit_alternate = 0;
}
if (Settings::values.frame_limit.GetValue() == 0) {
return;
}
if (Settings::values.frame_limit.GetValue() < 995 - SPEED_LIMIT_STEP) {
Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() +
SPEED_LIMIT_STEP);
} else {
if (Settings::values.frame_limit == 0) {
return;
}
if (Settings::values.frame_limit < 995 - SPEED_LIMIT_STEP) {
Settings::values.frame_limit += SPEED_LIMIT_STEP;
} else {
Settings::values.frame_limit = 0;
}
Settings::values.frame_limit = 0;
}
UpdateStatusBar();
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this),
&QShortcut::activated, this, [&] {
if (Settings::values.use_frame_limit_alternate) {
if (Settings::values.frame_limit_alternate == 0) {
Settings::values.frame_limit_alternate = 995;
} else if (Settings::values.frame_limit_alternate > SPEED_LIMIT_STEP) {
Settings::values.frame_limit_alternate -= SPEED_LIMIT_STEP;
}
} else {
if (Settings::values.frame_limit == 0) {
Settings::values.frame_limit = 995;
} else if (Settings::values.frame_limit > SPEED_LIMIT_STEP) {
Settings::values.frame_limit -= SPEED_LIMIT_STEP;
UpdateStatusBar();
}
if (Settings::values.frame_limit.GetValue() == 0) {
Settings::values.frame_limit = 995;
} else if (Settings::values.frame_limit.GetValue() > SPEED_LIMIT_STEP) {
Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() -
SPEED_LIMIT_STEP);
UpdateStatusBar();
}
UpdateStatusBar();
});
@@ -663,25 +650,26 @@ void GMainWindow::RestoreUIState() {
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
#if MICROPROFILE_ENABLED
microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry);
microProfileDialog->setVisible(UISettings::values.microprofile_visible);
microProfileDialog->setVisible(UISettings::values.microprofile_visible.GetValue());
#endif
ui->action_Cheats->setEnabled(false);
game_list->LoadInterfaceLayout();
ui->action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode);
ui->action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode.GetValue());
ToggleWindowMode();
ui->action_Fullscreen->setChecked(UISettings::values.fullscreen);
ui->action_Fullscreen->setChecked(UISettings::values.fullscreen.GetValue());
SyncMenuUISettings();
ui->action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar);
ui->action_Display_Dock_Widget_Headers->setChecked(
UISettings::values.display_titlebar.GetValue());
OnDisplayTitleBars(ui->action_Display_Dock_Widget_Headers->isChecked());
ui->action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar);
ui->action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar.GetValue());
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
ui->action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar);
ui->action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue());
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
}
@@ -717,6 +705,9 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::PopulatingCompleted, this,
[this] { multiplayer_state->UpdateGameList(game_list->GetModel()); });
connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
&GMainWindow::OnGameListOpenPerGameProperties);
connect(this, &GMainWindow::EmulationStarting, render_window,
&GRenderWindow::OnEmulationStarting);
connect(this, &GMainWindow::EmulationStopping, render_window,
@@ -752,6 +743,8 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui->action_Report_Compatibility, &QAction::triggered, this,
&GMainWindow::OnMenuReportCompatibility);
connect(ui->action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
connect(ui->action_Configure_Current_Game, &QAction::triggered, this,
&GMainWindow::OnConfigurePerGame);
connect(ui->action_Cheats, &QAction::triggered, this, &GMainWindow::OnCheats);
// View
@@ -1086,6 +1079,22 @@ void GMainWindow::BootGame(const QString& filename) {
Core::Movie::GetInstance().PrepareForPlayback(movie_playback_path.toStdString());
}
u64 title_id{0};
const std::string path = filename.toStdString();
const auto loader = Loader::GetLoader(path);
if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success) {
// Load per game settings
const std::string name{FileUtil::GetFilename(filename.toStdString())};
const std::string config_file_name =
title_id == 0 ? name : fmt::format("{:016X}", title_id);
Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
Settings::Apply();
LOG_INFO(Frontend, "Using per game config file for title id {}", config_file_name);
Settings::LogSettings();
}
// Save configurations
UpdateUISettings();
game_list->SaveInterfaceLayout();
@@ -1239,6 +1248,7 @@ void GMainWindow::ShutdownGame() {
ui->action_Stop->setEnabled(false);
ui->action_Restart->setEnabled(false);
ui->action_Cheats->setEnabled(false);
ui->action_Configure_Current_Game->setEnabled(false);
ui->action_Load_Amiibo->setEnabled(false);
ui->action_Remove_Amiibo->setEnabled(false);
ui->action_Report_Compatibility->setEnabled(false);
@@ -1398,26 +1408,42 @@ void GMainWindow::OnGameListOpenFolder(u64 data_id, GameListOpenTarget target) {
path = Service::AM::GetTitlePath(media_type, data_id) + "content/";
break;
}
case GameListOpenTarget::UPDATE_DATA:
case GameListOpenTarget::UPDATE_DATA: {
open_target = "Update Data";
path = Service::AM::GetTitlePath(Service::FS::MediaType::SDMC, data_id + 0xe00000000) +
"content/";
break;
case GameListOpenTarget::TEXTURE_DUMP:
}
case GameListOpenTarget::TEXTURE_DUMP: {
open_target = "Dumped Textures";
path = fmt::format("{}textures/{:016X}/",
FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), data_id);
break;
case GameListOpenTarget::TEXTURE_LOAD:
}
case GameListOpenTarget::TEXTURE_LOAD: {
open_target = "Custom Textures";
path = fmt::format("{}textures/{:016X}/",
FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), data_id);
break;
case GameListOpenTarget::MODS:
}
case GameListOpenTarget::MODS: {
open_target = "Mods";
path = fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
data_id);
break;
}
case GameListOpenTarget::DLC_DATA: {
open_target = "DLC Data";
path = fmt::format("{}Nintendo 3DS/00000000000000000000000000000000/"
"00000000000000000000000000000000/title/0004008c/{:08x}/content/",
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), data_id);
break;
}
case GameListOpenTarget::SHADER_CACHE: {
open_target = "Shader Cache";
path = FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir);
break;
}
default:
LOG_ERROR(Frontend, "Unexpected target {}", static_cast<int>(target));
return;
@@ -1529,6 +1555,19 @@ void GMainWindow::OnGameListShowList(bool show) {
game_list_placeholder->setVisible(!show);
};
void GMainWindow::OnGameListOpenPerGameProperties(const QString& file) {
const auto loader = Loader::GetLoader(file.toStdString());
u64 title_id{};
if (!loader || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
QMessageBox::information(this, tr("Properties"),
tr("The game properties could not be loaded."));
return;
}
OpenPerGameConfiguration(title_id, file);
}
void GMainWindow::OnMenuLoadFile() {
const QString extensions = QStringLiteral("*.").append(
GameList::supported_file_extensions.join(QStringLiteral(" *.")));
@@ -1654,6 +1693,7 @@ void GMainWindow::OnStartGame() {
ui->action_Stop->setEnabled(true);
ui->action_Restart->setEnabled(true);
ui->action_Cheats->setEnabled(true);
ui->action_Configure_Current_Game->setEnabled(true);
ui->action_Load_Amiibo->setEnabled(true);
ui->action_Report_Compatibility->setEnabled(true);
ui->action_Capture_Screenshot->setEnabled(true);
@@ -1676,6 +1716,7 @@ void GMainWindow::OnPauseGame() {
void GMainWindow::OnStopGame() {
ShutdownGame();
Settings::RestoreGlobalState(false);
}
void GMainWindow::OnLoadComplete() {
@@ -1769,7 +1810,7 @@ void GMainWindow::UpdateSecondaryWindowVisibility() {
if (!emulation_running) {
return;
}
if (Settings::values.layout_option == Settings::LayoutOption::SeparateWindows) {
if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows) {
secondary_window->RestoreGeometry();
secondary_window->show();
} else {
@@ -1800,7 +1841,7 @@ void GMainWindow::ChangeScreenLayout() {
void GMainWindow::ToggleScreenLayout() {
const Settings::LayoutOption new_layout = []() {
switch (Settings::values.layout_option) {
switch (Settings::values.layout_option.GetValue()) {
case Settings::LayoutOption::Default:
return Settings::LayoutOption::SingleScreen;
case Settings::LayoutOption::SingleScreen:
@@ -1812,7 +1853,8 @@ void GMainWindow::ToggleScreenLayout() {
case Settings::LayoutOption::SeparateWindows:
return Settings::LayoutOption::Default;
default:
LOG_ERROR(Frontend, "Unknown layout option {}", Settings::values.layout_option);
LOG_ERROR(Frontend, "Unknown layout option {}",
Settings::values.layout_option.GetValue());
return Settings::LayoutOption::Default;
}
}();
@@ -1864,6 +1906,7 @@ void GMainWindow::OnLoadState() {
}
void GMainWindow::OnConfigure() {
Settings::SetConfiguringGlobal(true);
ConfigureDialog configureDialog(this, hotkey_registry,
!multiplayer_state->IsHostingPublicRoom());
connect(&configureDialog, &ConfigureDialog::LanguageChanged, this,
@@ -1872,15 +1915,15 @@ void GMainWindow::OnConfigure() {
const int old_input_profile_index = Settings::values.current_input_profile_index;
const auto old_input_profiles = Settings::values.input_profiles;
const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps;
const bool old_discord_presence = UISettings::values.enable_discord_presence;
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
auto result = configureDialog.exec();
if (result == QDialog::Accepted) {
configureDialog.ApplyConfiguration();
InitializeHotkeys();
if (UISettings::values.theme != old_theme)
UpdateUITheme();
if (UISettings::values.enable_discord_presence != old_discord_presence)
SetDiscordEnabled(UISettings::values.enable_discord_presence);
if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence)
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
if (!multiplayer_state->IsHostingPublicRoom())
multiplayer_state->UpdateCredentials();
emit UpdateThemedIcons();
@@ -2057,24 +2100,26 @@ void GMainWindow::OnSaveMovie() {
void GMainWindow::OnCaptureScreenshot() {
OnPauseGame();
QString path = UISettings::values.screenshot_path;
if (!FileUtil::IsDirectory(path.toStdString())) {
if (!FileUtil::CreateFullPath(path.toStdString())) {
std::string path = UISettings::values.screenshot_path.GetValue();
if (!FileUtil::IsDirectory(path)) {
if (!FileUtil::CreateFullPath(path)) {
QMessageBox::information(this, tr("Invalid Screenshot Directory"),
tr("Cannot create specified screenshot directory. Screenshot "
"path is set back to its default value."));
path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir));
path.append(QStringLiteral("screenshots/"));
path = FileUtil::GetUserPath(FileUtil::UserPath::UserDir);
path.append("screenshots/");
UISettings::values.screenshot_path = path;
};
}
const QString filename = game_title.remove(QRegularExpression(QStringLiteral("[\\/:?\"<>|]")));
const QString timestamp =
QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yy_hh.mm.ss.z"));
path.append(QStringLiteral("/%1_%2.png").arg(filename, timestamp));
const std::string filename =
game_title.remove(QRegularExpression(QStringLiteral("[\\/:?\"<>|]"))).toStdString();
const std::string timestamp =
QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yy_hh.mm.ss.z")).toStdString();
path.append(fmt::format("/{}_{}.png", filename, timestamp));
auto* const screenshot_window = secondary_window->HasFocus() ? secondary_window : render_window;
screenshot_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path);
screenshot_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(),
QString::fromStdString(path));
OnStartGame();
}
@@ -2166,22 +2211,12 @@ void GMainWindow::UpdateStatusBar() {
auto results = Core::System::GetInstance().GetAndResetPerfStats();
if (Settings::values.use_frame_limit_alternate) {
if (Settings::values.frame_limit_alternate == 0) {
emu_speed_label->setText(
tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
} else {
emu_speed_label->setText(tr("Speed: %1% / %2%")
.arg(results.emulation_speed * 100.0, 0, 'f', 0)
.arg(Settings::values.frame_limit_alternate));
}
} else if (Settings::values.frame_limit == 0) {
if (Settings::values.frame_limit.GetValue() == 0) {
emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
} else {
emu_speed_label->setText(tr("Speed: %1% / %2%")
.arg(results.emulation_speed * 100.0, 0, 'f', 0)
.arg(Settings::values.frame_limit));
.arg(Settings::values.frame_limit.GetValue()));
}
game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0));
emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
@@ -2192,7 +2227,7 @@ void GMainWindow::UpdateStatusBar() {
}
void GMainWindow::HideMouseCursor() {
if (emu_thread == nullptr || UISettings::values.hide_mouse == false) {
if (emu_thread == nullptr || !UISettings::values.hide_mouse.GetValue()) {
mouse_hide_timer.stop();
ShowMouseCursor();
return;
@@ -2455,6 +2490,35 @@ void GMainWindow::OnLanguageChanged(const QString& locale) {
ui->action_Start->setText(tr("Continue"));
}
void GMainWindow::OnConfigurePerGame() {
u64 title_id{};
Core::System::GetInstance().GetAppLoader().ReadProgramId(title_id);
OpenPerGameConfiguration(title_id, game_path);
}
void GMainWindow::OpenPerGameConfiguration(u64 title_id, const QString& file_name) {
Core::System& system = Core::System::GetInstance();
Settings::SetConfiguringGlobal(false);
ConfigurePerGame dialog(this, title_id, file_name, system);
const auto result = dialog.exec();
if (result != QDialog::Accepted) {
Settings::RestoreGlobalState(system.IsPoweredOn());
return;
} else if (result == QDialog::Accepted) {
dialog.ApplyConfiguration();
}
// Do not cause the global config to write local settings into the config file
const bool is_powered_on = system.IsPoweredOn();
Settings::RestoreGlobalState(system.IsPoweredOn());
if (!is_powered_on) {
config->Save();
}
}
void GMainWindow::OnMoviePlaybackCompleted() {
OnPauseGame();
QMessageBox::information(this, tr("Playback Completed"), tr("Movie playback completed."));
@@ -2489,18 +2553,19 @@ void GMainWindow::UpdateUISettings() {
}
void GMainWindow::SyncMenuUISettings() {
ui->action_Screen_Layout_Default->setChecked(Settings::values.layout_option ==
ui->action_Screen_Layout_Default->setChecked(Settings::values.layout_option.GetValue() ==
Settings::LayoutOption::Default);
ui->action_Screen_Layout_Single_Screen->setChecked(Settings::values.layout_option ==
ui->action_Screen_Layout_Single_Screen->setChecked(Settings::values.layout_option.GetValue() ==
Settings::LayoutOption::SingleScreen);
ui->action_Screen_Layout_Large_Screen->setChecked(Settings::values.layout_option ==
ui->action_Screen_Layout_Large_Screen->setChecked(Settings::values.layout_option.GetValue() ==
Settings::LayoutOption::LargeScreen);
ui->action_Screen_Layout_Side_by_Side->setChecked(Settings::values.layout_option ==
ui->action_Screen_Layout_Side_by_Side->setChecked(Settings::values.layout_option.GetValue() ==
Settings::LayoutOption::SideScreen);
ui->action_Screen_Layout_Separate_Windows->setChecked(Settings::values.layout_option ==
Settings::LayoutOption::SeparateWindows);
ui->action_Screen_Layout_Swap_Screens->setChecked(Settings::values.swap_screen);
ui->action_Screen_Layout_Upright_Screens->setChecked(Settings::values.upright_screen);
ui->action_Screen_Layout_Separate_Windows->setChecked(
Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows);
ui->action_Screen_Layout_Swap_Screens->setChecked(Settings::values.swap_screen.GetValue());
ui->action_Screen_Layout_Upright_Screens->setChecked(
Settings::values.upright_screen.GetValue());
}
void GMainWindow::RetranslateStatusBar() {
@@ -2573,11 +2638,13 @@ int main(int argc, char* argv[]) {
// Register frontend applets
Frontend::RegisterDefaultApplets();
Core::System::GetInstance().RegisterMiiSelector(std::make_shared<QtMiiSelector>(main_window));
Core::System::GetInstance().RegisterSoftwareKeyboard(std::make_shared<QtKeyboard>(main_window));
Core::System& system = Core::System::GetInstance();
system.RegisterMiiSelector(std::make_shared<QtMiiSelector>(main_window));
system.RegisterSoftwareKeyboard(std::make_shared<QtKeyboard>(main_window));
// Register Qt image interface
Core::System::GetInstance().RegisterImageInterface(std::make_shared<QtImageInterface>());
system.RegisterImageInterface(std::make_shared<QtImageInterface>());
main_window.show();

View File

@@ -183,6 +183,8 @@ private slots:
void OnGameListOpenDirectory(const QString& directory);
void OnGameListAddDirectory();
void OnGameListShowList(bool show);
void OnGameListOpenPerGameProperties(const QString& file);
void OnConfigurePerGame();
void OnMenuLoadFile();
void OnMenuInstallCIA();
void OnUpdateProgress(std::size_t written, std::size_t total);
@@ -238,6 +240,7 @@ private:
void InstallCIA(QStringList filepaths);
void HideMouseCursor();
void ShowMouseCursor();
void OpenPerGameConfiguration(u64 title_id, const QString& file_name);
std::unique_ptr<Ui::MainWindow> ui;

View File

@@ -45,7 +45,7 @@
<x>0</x>
<y>0</y>
<width>1081</width>
<height>21</height>
<height>25</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
@@ -104,6 +104,7 @@
<addaction name="action_Report_Compatibility"/>
<addaction name="separator"/>
<addaction name="action_Configure"/>
<addaction name="action_Configure_Current_Game"/>
<addaction name="action_Cheats"/>
</widget>
<widget class="QMenu" name="menu_View">
@@ -239,20 +240,20 @@
</property>
</action>
<action name="action_Save">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Save</string>
</property>
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Save</string>
</property>
</action>
<action name="action_Load">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Load</string>
</property>
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Load</string>
</property>
</action>
<action name="action_FAQ">
<property name="text">
@@ -541,6 +542,14 @@
<string>Open Citra Folder</string>
</property>
</action>
<action name="action_Configure_Current_Game">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Configure Current Game...</string>
</property>
</action>
</widget>
<resources/>
<connections/>

View File

@@ -13,7 +13,7 @@
#include <QString>
#include <QStringList>
#include <QVector>
#include "common/common_types.h"
#include "common/settings.h"
namespace UISettings {
@@ -40,13 +40,13 @@ struct GameDir {
}
};
enum class GameListIconSize {
enum class GameListIconSize : u32 {
NoIcon, ///< Do not display icons
SmallIcon, ///< Display a small (24x24) icon
LargeIcon, ///< Display a large (48x48) icon
};
enum class GameListText {
enum class GameListText : s32 {
NoText = -1, ///< No text
FileName, ///< Display the file name of the entry
FullPath, ///< Display the full path of the entry
@@ -65,40 +65,41 @@ struct Values {
QByteArray gamelist_header_state;
QByteArray microprofile_geometry;
bool microprofile_visible;
Settings::Setting<bool> microprofile_visible{false, "microProfileDialogVisible"};
bool single_window_mode;
bool fullscreen;
bool display_titlebar;
bool show_filter_bar;
bool show_status_bar;
Settings::Setting<bool> single_window_mode{true, "singleWindowMode"};
Settings::Setting<bool> fullscreen{false, "fullscreen"};
Settings::Setting<bool> display_titlebar{true, "displayTitleBars"};
Settings::Setting<bool> show_filter_bar{true, "showFilterBar"};
Settings::Setting<bool> show_status_bar{true, "showStatusBar"};
bool confirm_before_closing;
bool first_start;
bool pause_when_in_background;
bool hide_mouse;
Settings::Setting<bool> confirm_before_closing{true, "confirmClose"};
Settings::Setting<bool> first_start{true, "firstStart"};
Settings::Setting<bool> pause_when_in_background{false, "pauseWhenInBackground"};
Settings::Setting<bool> hide_mouse{false, "hideInactiveMouse"};
bool updater_found;
bool update_on_close;
bool check_for_update_on_start;
Settings::Setting<bool> update_on_close{false, "update_on_close"};
Settings::Setting<bool> check_for_update_on_start{true, "check_for_update_on_start"};
// Discord RPC
bool enable_discord_presence;
Settings::Setting<bool> enable_discord_presence{true, "enable_discord_presence"};
// Game List
GameListIconSize game_list_icon_size;
GameListText game_list_row_1;
GameListText game_list_row_2;
bool game_list_hide_no_icon;
bool game_list_single_line_mode;
Settings::Setting<GameListIconSize> game_list_icon_size{GameListIconSize::LargeIcon,
"iconSize"};
Settings::Setting<GameListText> game_list_row_1{GameListText::TitleName, "row1"};
Settings::Setting<GameListText> game_list_row_2{GameListText::FileName, "row2"};
Settings::Setting<bool> game_list_hide_no_icon{false, "hideNoIcon"};
Settings::Setting<bool> game_list_single_line_mode{false, "singleLineMode"};
u16 screenshot_resolution_factor;
Settings::Setting<u16> screenshot_resolution_factor{0, "screenshot_resolution_factor"};
Settings::SwitchableSetting<std::string> screenshot_path{"", "screenshotPath"};
QString roms_path;
QString symbols_path;
QString movie_record_path;
QString movie_playback_path;
QString screenshot_path;
QString video_dumping_path;
QString game_dir_deprecated;
bool game_dir_deprecated_deepscan;
@@ -111,7 +112,7 @@ struct Values {
// Shortcut name <Shortcut, context>
std::vector<Shortcut> shortcuts;
uint32_t callout_flags;
Settings::Setting<u32> callout_flags{0, "calloutFlags"};
// multiplayer settings
QString nickname;
@@ -127,7 +128,7 @@ struct Values {
std::pair<std::vector<std::string>, std::vector<std::string>> ban_list;
// logging
bool show_console;
Settings::Setting<bool> show_console{false, "showConsole"};
};
extern Values values;