diff options
Diffstat (limited to 'src/yuzu')
40 files changed, 2948 insertions, 4001 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index fe98e3605e..2e4da696c0 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -143,6 +143,10 @@ add_executable(yuzu configuration/configure_web.ui configuration/input_profiles.cpp configuration/input_profiles.h + configuration/shared_translation.cpp + configuration/shared_translation.h + configuration/shared_widget.cpp + configuration/shared_widget.h debugger/console.cpp debugger/console.h debugger/controller.cpp @@ -231,6 +235,12 @@ if (WIN32 AND YUZU_CRASH_DUMPS) target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP) endif() +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_definitions(yuzu PRIVATE + $<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE> + ) +endif() + file(GLOB COMPAT_LIST ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 195d3556c9..f2ef34cbca 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -1,12 +1,14 @@ // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <algorithm> #include <array> #include <QKeySequence> #include <QSettings> #include "common/fs/fs.h" #include "common/fs/path_util.h" #include "common/settings.h" +#include "common/settings_common.h" #include "core/core.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/hid/controllers/npad.h" @@ -16,9 +18,8 @@ namespace FS = Common::FS; -Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) { - global = config_type == ConfigType::GlobalConfig; - +Config::Config(const std::string& config_name, ConfigType config_type) + : type(config_type), global{config_type == ConfigType::GlobalConfig} { Initialize(config_name); } @@ -89,10 +90,10 @@ const std::map<bool, QString> Config::use_docked_mode_texts_map = { {false, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, }; -const std::map<Settings::GPUAccuracy, QString> Config::gpu_accuracy_texts_map = { - {Settings::GPUAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))}, - {Settings::GPUAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))}, - {Settings::GPUAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))}, +const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = { + {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))}, + {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))}, + {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))}, }; const std::map<Settings::RendererBackend, QString> Config::renderer_backend_texts_map = { @@ -102,9 +103,9 @@ const std::map<Settings::RendererBackend, QString> Config::renderer_backend_text }; const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = { - {Settings::ShaderBackend::GLSL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, - {Settings::ShaderBackend::GLASM, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, - {Settings::ShaderBackend::SPIRV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, + {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, + {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, + {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, }; // This shouldn't have anything except static initializers (no functions). So @@ -171,66 +172,6 @@ bool Config::IsCustomConfig() { return type == ConfigType::PerGameConfig; } -/* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their - * usages later in this file. This allows explicit definition of some types that don't work - * nicely with the general version. - */ - -// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant, nor -// can it implicitly convert a QVariant back to a {std::,Q}string -template <> -void Config::ReadBasicSetting(Settings::Setting<std::string>& setting) { - const QString name = QString::fromStdString(setting.GetLabel()); - const auto default_value = QString::fromStdString(setting.GetDefault()); - if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { - setting.SetValue(default_value.toStdString()); - } else { - setting.SetValue(qt_config->value(name, default_value).toString().toStdString()); - } -} - -template <typename Type, bool ranged> -void Config::ReadBasicSetting(Settings::Setting<Type, ranged>& setting) { - const QString name = QString::fromStdString(setting.GetLabel()); - const Type default_value = setting.GetDefault(); - if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { - setting.SetValue(default_value); - } else { - setting.SetValue( - static_cast<QVariant>(qt_config->value(name, default_value)).value<Type>()); - } -} - -// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant -template <> -void Config::WriteBasicSetting(const Settings::Setting<std::string>& setting) { - const QString name = QString::fromStdString(setting.GetLabel()); - const std::string& value = setting.GetValue(); - qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); - qt_config->setValue(name, QString::fromStdString(value)); -} - -template <typename Type, bool ranged> -void Config::WriteBasicSetting(const Settings::Setting<Type, ranged>& setting) { - const QString name = QString::fromStdString(setting.GetLabel()); - const Type value = setting.GetValue(); - qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); - qt_config->setValue(name, value); -} - -template <typename Type, bool ranged> -void Config::WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& setting) { - const QString name = QString::fromStdString(setting.GetLabel()); - const Type& value = setting.GetValue(global); - if (!global) { - qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal()); - } - if (global || !setting.UsingGlobal()) { - qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); - qt_config->setValue(name, value); - } -} - void Config::ReadPlayerValue(std::size_t player_index) { const QString player_prefix = [this, player_index] { if (type == ConfigType::InputProfile) { @@ -351,15 +292,9 @@ void Config::ReadPlayerValue(std::size_t player_index) { player_motions = default_param; } } - - if (player_index == 0) { - ReadMousePanningValues(); - } } void Config::ReadDebugValues() { - ReadBasicSetting(Settings::values.debug_pad_enabled); - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; @@ -393,14 +328,6 @@ void Config::ReadDebugValues() { } } -void Config::ReadKeyboardValues() { - ReadBasicSetting(Settings::values.keyboard_enabled); -} - -void Config::ReadMouseValues() { - ReadBasicSetting(Settings::values.mouse_enabled); -} - void Config::ReadTouchscreenValues() { Settings::values.touchscreen.enabled = ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool(); @@ -414,9 +341,6 @@ void Config::ReadTouchscreenValues() { } void Config::ReadHidbusValues() { - Settings::values.enable_ring_controller = - ReadSetting(QStringLiteral("enable_ring_controller"), true).toBool(); - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); auto& ringcon_analogs = Settings::values.ringcon_analogs; @@ -430,20 +354,10 @@ void Config::ReadHidbusValues() { } } -void Config::ReadIrCameraValues() { - ReadBasicSetting(Settings::values.enable_ir_sensor); - ReadBasicSetting(Settings::values.ir_sensor_device); -} - void Config::ReadAudioValues() { qt_config->beginGroup(QStringLiteral("Audio")); - if (global) { - ReadBasicSetting(Settings::values.sink_id); - ReadBasicSetting(Settings::values.audio_output_device_id); - ReadBasicSetting(Settings::values.audio_input_device_id); - } - ReadGlobalSetting(Settings::values.volume); + ReadCategory(Settings::Category::Audio); qt_config->endGroup(); } @@ -451,11 +365,12 @@ void Config::ReadAudioValues() { void Config::ReadControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); + ReadCategory(Settings::Category::Controls); + Settings::values.players.SetGlobal(!IsCustomConfig()); for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { ReadPlayerValue(p); } - ReadGlobalSetting(Settings::values.use_docked_mode); // Disable docked mode if handheld is selected const auto controller_type = Settings::values.players.GetValue()[0].controller_type; @@ -464,50 +379,18 @@ void Config::ReadControlValues() { Settings::values.use_docked_mode.SetValue(false); } - ReadGlobalSetting(Settings::values.vibration_enabled); - ReadGlobalSetting(Settings::values.enable_accurate_vibrations); - ReadGlobalSetting(Settings::values.motion_enabled); if (IsCustomConfig()) { qt_config->endGroup(); return; } ReadDebugValues(); - ReadKeyboardValues(); - ReadMouseValues(); ReadTouchscreenValues(); - ReadMousePanningValues(); ReadMotionTouchValues(); ReadHidbusValues(); - ReadIrCameraValues(); - -#ifdef _WIN32 - ReadBasicSetting(Settings::values.enable_raw_input); -#else - Settings::values.enable_raw_input = false; -#endif - ReadBasicSetting(Settings::values.emulate_analog_keyboard); - ReadBasicSetting(Settings::values.enable_joycon_driver); - ReadBasicSetting(Settings::values.enable_procon_driver); - ReadBasicSetting(Settings::values.random_amiibo_id); - - ReadBasicSetting(Settings::values.tas_enable); - ReadBasicSetting(Settings::values.tas_loop); - ReadBasicSetting(Settings::values.pause_tas_on_load); - - ReadBasicSetting(Settings::values.controller_navigation); qt_config->endGroup(); } -void Config::ReadMousePanningValues() { - ReadBasicSetting(Settings::values.mouse_panning); - ReadBasicSetting(Settings::values.mouse_panning_x_sensitivity); - ReadBasicSetting(Settings::values.mouse_panning_y_sensitivity); - ReadBasicSetting(Settings::values.mouse_panning_deadzone_counterweight); - ReadBasicSetting(Settings::values.mouse_panning_decay_strength); - ReadBasicSetting(Settings::values.mouse_panning_min_decay); -} - void Config::ReadMotionTouchValues() { int num_touch_from_button_maps = qt_config->beginReadArray(QStringLiteral("touch_from_button_maps")); @@ -541,19 +424,14 @@ void Config::ReadMotionTouchValues() { } qt_config->endArray(); - ReadBasicSetting(Settings::values.touch_device); - ReadBasicSetting(Settings::values.touch_from_button_map_index); Settings::values.touch_from_button_map_index = std::clamp( Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); - ReadBasicSetting(Settings::values.udp_input_servers); - ReadBasicSetting(Settings::values.enable_udp_controller); } void Config::ReadCoreValues() { qt_config->beginGroup(QStringLiteral("Core")); - ReadGlobalSetting(Settings::values.use_multi_core); - ReadGlobalSetting(Settings::values.use_unsafe_extended_memory_layout); + ReadCategory(Settings::Category::Core); qt_config->endGroup(); } @@ -561,7 +439,6 @@ void Config::ReadCoreValues() { void Config::ReadDataStorageValues() { qt_config->beginGroup(QStringLiteral("Data Storage")); - ReadBasicSetting(Settings::values.use_virtual_sd); FS::SetYuzuPath( FS::YuzuPath::NANDDir, qt_config @@ -597,9 +474,7 @@ void Config::ReadDataStorageValues() { .toString() .toStdString()); - ReadBasicSetting(Settings::values.gamecard_inserted); - ReadBasicSetting(Settings::values.gamecard_current_game); - ReadBasicSetting(Settings::values.gamecard_path); + ReadCategory(Settings::Category::DataStorage); qt_config->endGroup(); } @@ -611,29 +486,17 @@ void Config::ReadDebuggingValues() { Settings::values.record_frame_times = qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); - ReadBasicSetting(Settings::values.use_gdbstub); - ReadBasicSetting(Settings::values.gdbstub_port); - ReadBasicSetting(Settings::values.program_args); - ReadBasicSetting(Settings::values.dump_exefs); - ReadBasicSetting(Settings::values.dump_nso); - ReadBasicSetting(Settings::values.enable_fs_access_log); - ReadBasicSetting(Settings::values.reporting_services); - ReadBasicSetting(Settings::values.quest_flag); - ReadBasicSetting(Settings::values.disable_macro_jit); - ReadBasicSetting(Settings::values.disable_macro_hle); - ReadBasicSetting(Settings::values.extended_logging); - ReadBasicSetting(Settings::values.use_debug_asserts); - ReadBasicSetting(Settings::values.use_auto_stub); - ReadBasicSetting(Settings::values.enable_all_controllers); - ReadBasicSetting(Settings::values.create_crash_dumps); - ReadBasicSetting(Settings::values.perform_vulkan_check); + ReadCategory(Settings::Category::Debugging); + ReadCategory(Settings::Category::DebuggingGraphics); qt_config->endGroup(); } void Config::ReadServiceValues() { qt_config->beginGroup(QStringLiteral("Services")); - ReadBasicSetting(Settings::values.network_interface); + + ReadCategory(Settings::Category::Services); + qt_config->endGroup(); } @@ -659,8 +522,7 @@ void Config::ReadDisabledAddOnValues() { void Config::ReadMiscellaneousValues() { qt_config->beginGroup(QStringLiteral("Miscellaneous")); - ReadBasicSetting(Settings::values.log_filter); - ReadBasicSetting(Settings::values.use_dev_keys); + ReadCategory(Settings::Category::Miscellaneous); qt_config->endGroup(); } @@ -710,36 +572,9 @@ void Config::ReadPathValues() { void Config::ReadCpuValues() { qt_config->beginGroup(QStringLiteral("Cpu")); - ReadBasicSetting(Settings::values.cpu_accuracy_first_time); - if (Settings::values.cpu_accuracy_first_time) { - Settings::values.cpu_accuracy.SetValue(Settings::values.cpu_accuracy.GetDefault()); - Settings::values.cpu_accuracy_first_time.SetValue(false); - } else { - ReadGlobalSetting(Settings::values.cpu_accuracy); - } - - ReadGlobalSetting(Settings::values.cpuopt_unsafe_unfuse_fma); - ReadGlobalSetting(Settings::values.cpuopt_unsafe_reduce_fp_error); - ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr); - ReadGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan); - ReadGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); - ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_global_monitor); - - if (global) { - ReadBasicSetting(Settings::values.cpu_debug_mode); - ReadBasicSetting(Settings::values.cpuopt_page_tables); - ReadBasicSetting(Settings::values.cpuopt_block_linking); - ReadBasicSetting(Settings::values.cpuopt_return_stack_buffer); - ReadBasicSetting(Settings::values.cpuopt_fast_dispatcher); - ReadBasicSetting(Settings::values.cpuopt_context_elimination); - ReadBasicSetting(Settings::values.cpuopt_const_prop); - ReadBasicSetting(Settings::values.cpuopt_misc_ir); - ReadBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); - ReadBasicSetting(Settings::values.cpuopt_fastmem); - ReadBasicSetting(Settings::values.cpuopt_fastmem_exclusives); - ReadBasicSetting(Settings::values.cpuopt_recompile_exclusives); - ReadBasicSetting(Settings::values.cpuopt_ignore_memory_aborts); - } + ReadCategory(Settings::Category::Cpu); + ReadCategory(Settings::Category::CpuDebug); + ReadCategory(Settings::Category::CpuUnsafe); qt_config->endGroup(); } @@ -747,47 +582,9 @@ void Config::ReadCpuValues() { void Config::ReadRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); - ReadGlobalSetting(Settings::values.renderer_backend); - ReadGlobalSetting(Settings::values.async_presentation); - ReadGlobalSetting(Settings::values.renderer_force_max_clock); - ReadGlobalSetting(Settings::values.vulkan_device); - ReadGlobalSetting(Settings::values.fullscreen_mode); - ReadGlobalSetting(Settings::values.aspect_ratio); - ReadGlobalSetting(Settings::values.resolution_setup); - ReadGlobalSetting(Settings::values.scaling_filter); - ReadGlobalSetting(Settings::values.fsr_sharpening_slider); - ReadGlobalSetting(Settings::values.anti_aliasing); - ReadGlobalSetting(Settings::values.max_anisotropy); - ReadGlobalSetting(Settings::values.speed_limit); - ReadGlobalSetting(Settings::values.use_disk_shader_cache); - ReadGlobalSetting(Settings::values.gpu_accuracy); - ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); - ReadGlobalSetting(Settings::values.nvdec_emulation); - ReadGlobalSetting(Settings::values.accelerate_astc); - ReadGlobalSetting(Settings::values.async_astc); - ReadGlobalSetting(Settings::values.astc_recompression); - ReadGlobalSetting(Settings::values.use_reactive_flushing); - ReadGlobalSetting(Settings::values.shader_backend); - ReadGlobalSetting(Settings::values.use_asynchronous_shaders); - ReadGlobalSetting(Settings::values.use_fast_gpu_time); - ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); - ReadGlobalSetting(Settings::values.enable_compute_pipelines); - ReadGlobalSetting(Settings::values.use_video_framerate); - ReadGlobalSetting(Settings::values.barrier_feedback_loops); - ReadGlobalSetting(Settings::values.bg_red); - ReadGlobalSetting(Settings::values.bg_green); - ReadGlobalSetting(Settings::values.bg_blue); - - if (global) { - Settings::values.vsync_mode.SetValue(static_cast<Settings::VSyncMode>( - ReadSetting(QString::fromStdString(Settings::values.vsync_mode.GetLabel()), - static_cast<u32>(Settings::values.vsync_mode.GetDefault())) - .value<u32>())); - ReadBasicSetting(Settings::values.renderer_debug); - ReadBasicSetting(Settings::values.renderer_shader_feedback); - ReadBasicSetting(Settings::values.enable_nsight_aftermath); - ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks); - } + ReadCategory(Settings::Category::Renderer); + ReadCategory(Settings::Category::RendererAdvanced); + ReadCategory(Settings::Category::RendererDebug); qt_config->endGroup(); } @@ -834,41 +631,8 @@ void Config::ReadShortcutValues() { void Config::ReadSystemValues() { qt_config->beginGroup(QStringLiteral("System")); - ReadGlobalSetting(Settings::values.language_index); - - ReadGlobalSetting(Settings::values.region_index); - - ReadGlobalSetting(Settings::values.time_zone_index); - - bool rng_seed_enabled; - ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false); - bool rng_seed_global = - global || qt_config->value(QStringLiteral("rng_seed/use_global"), true).toBool(); - Settings::values.rng_seed.SetGlobal(rng_seed_global); - if (global || !rng_seed_global) { - if (rng_seed_enabled) { - Settings::values.rng_seed.SetValue(ReadSetting(QStringLiteral("rng_seed"), 0).toUInt()); - } else { - Settings::values.rng_seed.SetValue(std::nullopt); - } - } - - if (global) { - ReadBasicSetting(Settings::values.current_user); - Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0, - Service::Account::MAX_USERS - 1); - - const auto custom_rtc_enabled = - ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool(); - if (custom_rtc_enabled) { - Settings::values.custom_rtc = ReadSetting(QStringLiteral("custom_rtc"), 0).toLongLong(); - } else { - Settings::values.custom_rtc = std::nullopt; - } - ReadBasicSetting(Settings::values.device_name); - } - - ReadGlobalSetting(Settings::values.sound_index); + ReadCategory(Settings::Category::System); + ReadCategory(Settings::Category::SystemAudio); qt_config->endGroup(); } @@ -881,8 +645,6 @@ void Config::ReadUIValues() { QStringLiteral("theme"), QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second)) .toString(); - ReadBasicSetting(UISettings::values.enable_discord_presence); - ReadBasicSetting(UISettings::values.select_user_on_boot); ReadUIGamelistValues(); ReadUILayoutValues(); @@ -891,20 +653,8 @@ void Config::ReadUIValues() { ReadShortcutValues(); ReadMultiplayerValues(); - ReadBasicSetting(UISettings::values.single_window_mode); - ReadBasicSetting(UISettings::values.fullscreen); - ReadBasicSetting(UISettings::values.display_titlebar); - ReadBasicSetting(UISettings::values.show_filter_bar); - ReadBasicSetting(UISettings::values.show_status_bar); - ReadBasicSetting(UISettings::values.confirm_before_closing); - ReadBasicSetting(UISettings::values.first_start); - ReadBasicSetting(UISettings::values.callout_flags); - ReadBasicSetting(UISettings::values.show_console); - ReadBasicSetting(UISettings::values.pause_when_in_background); - ReadBasicSetting(UISettings::values.mute_when_in_background); - ReadBasicSetting(UISettings::values.hide_mouse); - ReadBasicSetting(UISettings::values.controller_applet_disabled); - ReadBasicSetting(UISettings::values.disable_web_applet); + ReadCategory(Settings::Category::Ui); + ReadCategory(Settings::Category::UiGeneral); qt_config->endGroup(); } @@ -912,16 +662,8 @@ void Config::ReadUIValues() { void Config::ReadUIGamelistValues() { qt_config->beginGroup(QStringLiteral("UIGameList")); - ReadBasicSetting(UISettings::values.show_add_ons); - ReadBasicSetting(UISettings::values.show_compat); - ReadBasicSetting(UISettings::values.show_size); - ReadBasicSetting(UISettings::values.show_types); - ReadBasicSetting(UISettings::values.game_icon_size); - ReadBasicSetting(UISettings::values.folder_icon_size); - ReadBasicSetting(UISettings::values.row_1_text_id); - ReadBasicSetting(UISettings::values.row_2_text_id); - ReadBasicSetting(UISettings::values.cache_game_list); - ReadBasicSetting(UISettings::values.favorites_expanded); + ReadCategory(Settings::Category::UiGameList); + const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); for (int i = 0; i < favorites_size; i++) { qt_config->setArrayIndex(i); @@ -944,7 +686,8 @@ void Config::ReadUILayoutValues() { ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray(); UISettings::values.microprofile_geometry = ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray(); - ReadBasicSetting(UISettings::values.microprofile_visible); + + ReadCategory(Settings::Category::UiLayout); qt_config->endGroup(); } @@ -952,10 +695,7 @@ void Config::ReadUILayoutValues() { void Config::ReadWebServiceValues() { qt_config->beginGroup(QStringLiteral("WebService")); - ReadBasicSetting(Settings::values.enable_telemetry); - ReadBasicSetting(Settings::values.web_api_url); - ReadBasicSetting(Settings::values.yuzu_username); - ReadBasicSetting(Settings::values.yuzu_token); + ReadCategory(Settings::Category::WebService); qt_config->endGroup(); } @@ -963,17 +703,7 @@ void Config::ReadWebServiceValues() { void Config::ReadMultiplayerValues() { qt_config->beginGroup(QStringLiteral("Multiplayer")); - ReadBasicSetting(UISettings::values.multiplayer_nickname); - ReadBasicSetting(UISettings::values.multiplayer_ip); - ReadBasicSetting(UISettings::values.multiplayer_port); - ReadBasicSetting(UISettings::values.multiplayer_room_nickname); - ReadBasicSetting(UISettings::values.multiplayer_room_name); - ReadBasicSetting(UISettings::values.multiplayer_room_port); - ReadBasicSetting(UISettings::values.multiplayer_host_type); - ReadBasicSetting(UISettings::values.multiplayer_port); - ReadBasicSetting(UISettings::values.multiplayer_max_player); - ReadBasicSetting(UISettings::values.multiplayer_game_id); - ReadBasicSetting(UISettings::values.multiplayer_room_description); + ReadCategory(Settings::Category::Multiplayer); // Read ban list back int size = qt_config->beginReadArray(QStringLiteral("username_ban_list")); @@ -996,11 +726,21 @@ void Config::ReadMultiplayerValues() { qt_config->endGroup(); } +void Config::ReadNetworkValues() { + qt_config->beginGroup( + QString::fromUtf8(Settings::TranslateCategory(Settings::Category::Network))); + + ReadCategory(Settings::Category::Network); + + qt_config->endGroup(); +} + void Config::ReadValues() { if (global) { ReadDataStorageValues(); ReadDebuggingValues(); ReadDisabledAddOnValues(); + ReadNetworkValues(); ReadServiceValues(); ReadUIValues(); ReadWebServiceValues(); @@ -1077,14 +817,9 @@ void Config::SavePlayerValue(std::size_t player_index) { QString::fromStdString(player.motions[i]), QString::fromStdString(default_param)); } - - if (player_index == 0) { - SaveMousePanningValues(); - } } void Config::SaveDebugValues() { - WriteBasicSetting(Settings::values.debug_pad_enabled); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); WriteSetting(QStringLiteral("debug_pad_") + @@ -1103,10 +838,6 @@ void Config::SaveDebugValues() { } } -void Config::SaveMouseValues() { - WriteBasicSetting(Settings::values.mouse_enabled); -} - void Config::SaveTouchscreenValues() { const auto& touchscreen = Settings::values.touchscreen; @@ -1117,21 +848,7 @@ void Config::SaveTouchscreenValues() { WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); } -void Config::SaveMousePanningValues() { - // Don't overwrite values.mouse_panning - WriteBasicSetting(Settings::values.mouse_panning_x_sensitivity); - WriteBasicSetting(Settings::values.mouse_panning_y_sensitivity); - WriteBasicSetting(Settings::values.mouse_panning_deadzone_counterweight); - WriteBasicSetting(Settings::values.mouse_panning_decay_strength); - WriteBasicSetting(Settings::values.mouse_panning_min_decay); -} - void Config::SaveMotionTouchValues() { - WriteBasicSetting(Settings::values.touch_device); - WriteBasicSetting(Settings::values.touch_from_button_map_index); - WriteBasicSetting(Settings::values.udp_input_servers); - WriteBasicSetting(Settings::values.enable_udp_controller); - qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { qt_config->setArrayIndex(static_cast<int>(p)); @@ -1152,8 +869,6 @@ void Config::SaveMotionTouchValues() { } void Config::SaveHidbusValues() { - WriteBasicSetting(Settings::values.enable_ring_controller); - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); WriteSetting(QStringLiteral("ring_controller"), @@ -1161,11 +876,6 @@ void Config::SaveHidbusValues() { QString::fromStdString(default_param)); } -void Config::SaveIrCameraValues() { - WriteBasicSetting(Settings::values.enable_ir_sensor); - WriteBasicSetting(Settings::values.ir_sensor_device); -} - void Config::SaveValues() { if (global) { SaveDataStorageValues(); @@ -1182,18 +892,14 @@ void Config::SaveValues() { SaveRendererValues(); SaveAudioValues(); SaveSystemValues(); + qt_config->sync(); } void Config::SaveAudioValues() { qt_config->beginGroup(QStringLiteral("Audio")); - if (global) { - WriteBasicSetting(Settings::values.sink_id); - WriteBasicSetting(Settings::values.audio_output_device_id); - WriteBasicSetting(Settings::values.audio_input_device_id); - } - WriteGlobalSetting(Settings::values.volume); + WriteCategory(Settings::Category::Audio); qt_config->endGroup(); } @@ -1201,6 +907,8 @@ void Config::SaveAudioValues() { void Config::SaveControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); + WriteCategory(Settings::Category::Controls); + Settings::values.players.SetGlobal(!IsCustomConfig()); for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { SavePlayerValue(p); @@ -1210,28 +918,9 @@ void Config::SaveControlValues() { return; } SaveDebugValues(); - SaveMouseValues(); SaveTouchscreenValues(); - SaveMousePanningValues(); SaveMotionTouchValues(); SaveHidbusValues(); - SaveIrCameraValues(); - - WriteGlobalSetting(Settings::values.use_docked_mode); - WriteGlobalSetting(Settings::values.vibration_enabled); - WriteGlobalSetting(Settings::values.enable_accurate_vibrations); - WriteGlobalSetting(Settings::values.motion_enabled); - WriteBasicSetting(Settings::values.enable_raw_input); - WriteBasicSetting(Settings::values.enable_joycon_driver); - WriteBasicSetting(Settings::values.enable_procon_driver); - WriteBasicSetting(Settings::values.random_amiibo_id); - WriteBasicSetting(Settings::values.keyboard_enabled); - WriteBasicSetting(Settings::values.emulate_analog_keyboard); - WriteBasicSetting(Settings::values.controller_navigation); - - WriteBasicSetting(Settings::values.tas_enable); - WriteBasicSetting(Settings::values.tas_loop); - WriteBasicSetting(Settings::values.pause_tas_on_load); qt_config->endGroup(); } @@ -1239,8 +928,7 @@ void Config::SaveControlValues() { void Config::SaveCoreValues() { qt_config->beginGroup(QStringLiteral("Core")); - WriteGlobalSetting(Settings::values.use_multi_core); - WriteGlobalSetting(Settings::values.use_unsafe_extended_memory_layout); + WriteCategory(Settings::Category::Core); qt_config->endGroup(); } @@ -1248,7 +936,6 @@ void Config::SaveCoreValues() { void Config::SaveDataStorageValues() { qt_config->beginGroup(QStringLiteral("Data Storage")); - WriteBasicSetting(Settings::values.use_virtual_sd); WriteSetting(QStringLiteral("nand_directory"), QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)), QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); @@ -1265,9 +952,7 @@ void Config::SaveDataStorageValues() { QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)), QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); - WriteBasicSetting(Settings::values.gamecard_inserted); - WriteBasicSetting(Settings::values.gamecard_current_game); - WriteBasicSetting(Settings::values.gamecard_path); + WriteCategory(Settings::Category::DataStorage); qt_config->endGroup(); } @@ -1277,19 +962,9 @@ void Config::SaveDebuggingValues() { // Intentionally not using the QT default setting as this is intended to be changed in the ini qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); - WriteBasicSetting(Settings::values.use_gdbstub); - WriteBasicSetting(Settings::values.gdbstub_port); - WriteBasicSetting(Settings::values.program_args); - WriteBasicSetting(Settings::values.dump_exefs); - WriteBasicSetting(Settings::values.dump_nso); - WriteBasicSetting(Settings::values.enable_fs_access_log); - WriteBasicSetting(Settings::values.quest_flag); - WriteBasicSetting(Settings::values.use_debug_asserts); - WriteBasicSetting(Settings::values.disable_macro_jit); - WriteBasicSetting(Settings::values.disable_macro_hle); - WriteBasicSetting(Settings::values.enable_all_controllers); - WriteBasicSetting(Settings::values.create_crash_dumps); - WriteBasicSetting(Settings::values.perform_vulkan_check); + + WriteCategory(Settings::Category::Debugging); + WriteCategory(Settings::Category::DebuggingGraphics); qt_config->endGroup(); } @@ -1297,7 +972,7 @@ void Config::SaveDebuggingValues() { void Config::SaveNetworkValues() { qt_config->beginGroup(QStringLiteral("Services")); - WriteBasicSetting(Settings::values.network_interface); + WriteCategory(Settings::Category::Network); qt_config->endGroup(); } @@ -1324,8 +999,7 @@ void Config::SaveDisabledAddOnValues() { void Config::SaveMiscellaneousValues() { qt_config->beginGroup(QStringLiteral("Miscellaneous")); - WriteBasicSetting(Settings::values.log_filter); - WriteBasicSetting(Settings::values.use_dev_keys); + WriteCategory(Settings::Category::Miscellaneous); qt_config->endGroup(); } @@ -1353,34 +1027,9 @@ void Config::SavePathValues() { void Config::SaveCpuValues() { qt_config->beginGroup(QStringLiteral("Cpu")); - WriteBasicSetting(Settings::values.cpu_accuracy_first_time); - WriteSetting(QStringLiteral("cpu_accuracy"), - static_cast<u32>(Settings::values.cpu_accuracy.GetValue(global)), - static_cast<u32>(Settings::values.cpu_accuracy.GetDefault()), - Settings::values.cpu_accuracy.UsingGlobal()); - - WriteGlobalSetting(Settings::values.cpuopt_unsafe_unfuse_fma); - WriteGlobalSetting(Settings::values.cpuopt_unsafe_reduce_fp_error); - WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr); - WriteGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan); - WriteGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); - WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_global_monitor); - - if (global) { - WriteBasicSetting(Settings::values.cpu_debug_mode); - WriteBasicSetting(Settings::values.cpuopt_page_tables); - WriteBasicSetting(Settings::values.cpuopt_block_linking); - WriteBasicSetting(Settings::values.cpuopt_return_stack_buffer); - WriteBasicSetting(Settings::values.cpuopt_fast_dispatcher); - WriteBasicSetting(Settings::values.cpuopt_context_elimination); - WriteBasicSetting(Settings::values.cpuopt_const_prop); - WriteBasicSetting(Settings::values.cpuopt_misc_ir); - WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); - WriteBasicSetting(Settings::values.cpuopt_fastmem); - WriteBasicSetting(Settings::values.cpuopt_fastmem_exclusives); - WriteBasicSetting(Settings::values.cpuopt_recompile_exclusives); - WriteBasicSetting(Settings::values.cpuopt_ignore_memory_aborts); - } + WriteCategory(Settings::Category::Cpu); + WriteCategory(Settings::Category::CpuDebug); + WriteCategory(Settings::Category::CpuUnsafe); qt_config->endGroup(); } @@ -1388,76 +1037,9 @@ void Config::SaveCpuValues() { void Config::SaveRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); - WriteSetting(QString::fromStdString(Settings::values.renderer_backend.GetLabel()), - static_cast<u32>(Settings::values.renderer_backend.GetValue(global)), - static_cast<u32>(Settings::values.renderer_backend.GetDefault()), - Settings::values.renderer_backend.UsingGlobal()); - WriteGlobalSetting(Settings::values.async_presentation); - WriteGlobalSetting(Settings::values.renderer_force_max_clock); - WriteGlobalSetting(Settings::values.vulkan_device); - WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()), - static_cast<u32>(Settings::values.fullscreen_mode.GetValue(global)), - static_cast<u32>(Settings::values.fullscreen_mode.GetDefault()), - Settings::values.fullscreen_mode.UsingGlobal()); - WriteGlobalSetting(Settings::values.aspect_ratio); - WriteSetting(QString::fromStdString(Settings::values.resolution_setup.GetLabel()), - static_cast<u32>(Settings::values.resolution_setup.GetValue(global)), - static_cast<u32>(Settings::values.resolution_setup.GetDefault()), - Settings::values.resolution_setup.UsingGlobal()); - WriteSetting(QString::fromStdString(Settings::values.scaling_filter.GetLabel()), - static_cast<u32>(Settings::values.scaling_filter.GetValue(global)), - static_cast<u32>(Settings::values.scaling_filter.GetDefault()), - Settings::values.scaling_filter.UsingGlobal()); - WriteSetting(QString::fromStdString(Settings::values.fsr_sharpening_slider.GetLabel()), - static_cast<u32>(Settings::values.fsr_sharpening_slider.GetValue(global)), - static_cast<u32>(Settings::values.fsr_sharpening_slider.GetDefault()), - Settings::values.fsr_sharpening_slider.UsingGlobal()); - WriteSetting(QString::fromStdString(Settings::values.anti_aliasing.GetLabel()), - static_cast<u32>(Settings::values.anti_aliasing.GetValue(global)), - static_cast<u32>(Settings::values.anti_aliasing.GetDefault()), - Settings::values.anti_aliasing.UsingGlobal()); - WriteGlobalSetting(Settings::values.max_anisotropy); - WriteGlobalSetting(Settings::values.speed_limit); - WriteGlobalSetting(Settings::values.use_disk_shader_cache); - WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), - static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)), - static_cast<u32>(Settings::values.gpu_accuracy.GetDefault()), - Settings::values.gpu_accuracy.UsingGlobal()); - WriteGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); - WriteSetting(QString::fromStdString(Settings::values.nvdec_emulation.GetLabel()), - static_cast<u32>(Settings::values.nvdec_emulation.GetValue(global)), - static_cast<u32>(Settings::values.nvdec_emulation.GetDefault()), - Settings::values.nvdec_emulation.UsingGlobal()); - WriteGlobalSetting(Settings::values.accelerate_astc); - WriteGlobalSetting(Settings::values.async_astc); - WriteSetting(QString::fromStdString(Settings::values.astc_recompression.GetLabel()), - static_cast<u32>(Settings::values.astc_recompression.GetValue(global)), - static_cast<u32>(Settings::values.astc_recompression.GetDefault()), - Settings::values.astc_recompression.UsingGlobal()); - WriteGlobalSetting(Settings::values.use_reactive_flushing); - WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()), - static_cast<u32>(Settings::values.shader_backend.GetValue(global)), - static_cast<u32>(Settings::values.shader_backend.GetDefault()), - Settings::values.shader_backend.UsingGlobal()); - WriteGlobalSetting(Settings::values.use_asynchronous_shaders); - WriteGlobalSetting(Settings::values.use_fast_gpu_time); - WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); - WriteGlobalSetting(Settings::values.enable_compute_pipelines); - WriteGlobalSetting(Settings::values.use_video_framerate); - WriteGlobalSetting(Settings::values.barrier_feedback_loops); - WriteGlobalSetting(Settings::values.bg_red); - WriteGlobalSetting(Settings::values.bg_green); - WriteGlobalSetting(Settings::values.bg_blue); - - if (global) { - WriteSetting(QString::fromStdString(Settings::values.vsync_mode.GetLabel()), - static_cast<u32>(Settings::values.vsync_mode.GetValue()), - static_cast<u32>(Settings::values.vsync_mode.GetDefault())); - WriteBasicSetting(Settings::values.renderer_debug); - WriteBasicSetting(Settings::values.renderer_shader_feedback); - WriteBasicSetting(Settings::values.enable_nsight_aftermath); - WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks); - } + WriteCategory(Settings::Category::Renderer); + WriteCategory(Settings::Category::RendererAdvanced); + WriteCategory(Settings::Category::RendererDebug); qt_config->endGroup(); } @@ -1465,9 +1047,9 @@ void Config::SaveRendererValues() { void Config::SaveScreenshotValues() { qt_config->beginGroup(QStringLiteral("Screenshots")); - WriteBasicSetting(UISettings::values.enable_screenshot_save_as); WriteSetting(QStringLiteral("screenshot_path"), QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))); + WriteCategory(Settings::Category::Screenshots); qt_config->endGroup(); } @@ -1498,27 +1080,8 @@ void Config::SaveShortcutValues() { void Config::SaveSystemValues() { qt_config->beginGroup(QStringLiteral("System")); - WriteGlobalSetting(Settings::values.language_index); - WriteGlobalSetting(Settings::values.region_index); - WriteGlobalSetting(Settings::values.time_zone_index); - - WriteSetting(QStringLiteral("rng_seed_enabled"), - Settings::values.rng_seed.GetValue(global).has_value(), false, - Settings::values.rng_seed.UsingGlobal()); - WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0), - 0, Settings::values.rng_seed.UsingGlobal()); - - if (global) { - WriteBasicSetting(Settings::values.current_user); - - WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(), - false); - WriteSetting(QStringLiteral("custom_rtc"), - QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0); - WriteBasicSetting(Settings::values.device_name); - } - - WriteGlobalSetting(Settings::values.sound_index); + WriteCategory(Settings::Category::System); + WriteCategory(Settings::Category::SystemAudio); qt_config->endGroup(); } @@ -1526,10 +1089,10 @@ void Config::SaveSystemValues() { void Config::SaveUIValues() { qt_config->beginGroup(QStringLiteral("UI")); + WriteCategory(Settings::Category::Ui); + WriteSetting(QStringLiteral("theme"), UISettings::values.theme, QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second)); - WriteBasicSetting(UISettings::values.enable_discord_presence); - WriteBasicSetting(UISettings::values.select_user_on_boot); SaveUIGamelistValues(); SaveUILayoutValues(); @@ -1538,37 +1101,14 @@ void Config::SaveUIValues() { SaveShortcutValues(); SaveMultiplayerValues(); - WriteBasicSetting(UISettings::values.single_window_mode); - WriteBasicSetting(UISettings::values.fullscreen); - WriteBasicSetting(UISettings::values.display_titlebar); - WriteBasicSetting(UISettings::values.show_filter_bar); - WriteBasicSetting(UISettings::values.show_status_bar); - WriteBasicSetting(UISettings::values.confirm_before_closing); - WriteBasicSetting(UISettings::values.first_start); - WriteBasicSetting(UISettings::values.callout_flags); - WriteBasicSetting(UISettings::values.show_console); - WriteBasicSetting(UISettings::values.pause_when_in_background); - WriteBasicSetting(UISettings::values.mute_when_in_background); - WriteBasicSetting(UISettings::values.hide_mouse); - WriteBasicSetting(UISettings::values.controller_applet_disabled); - WriteBasicSetting(UISettings::values.disable_web_applet); - qt_config->endGroup(); } void Config::SaveUIGamelistValues() { qt_config->beginGroup(QStringLiteral("UIGameList")); - WriteBasicSetting(UISettings::values.show_add_ons); - WriteBasicSetting(UISettings::values.show_compat); - WriteBasicSetting(UISettings::values.show_size); - WriteBasicSetting(UISettings::values.show_types); - WriteBasicSetting(UISettings::values.game_icon_size); - WriteBasicSetting(UISettings::values.folder_icon_size); - WriteBasicSetting(UISettings::values.row_1_text_id); - WriteBasicSetting(UISettings::values.row_2_text_id); - WriteBasicSetting(UISettings::values.cache_game_list); - WriteBasicSetting(UISettings::values.favorites_expanded); + WriteCategory(Settings::Category::UiGameList); + qt_config->beginWriteArray(QStringLiteral("favorites")); for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { qt_config->setArrayIndex(i); @@ -1589,7 +1129,8 @@ void Config::SaveUILayoutValues() { WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state); WriteSetting(QStringLiteral("microProfileDialogGeometry"), UISettings::values.microprofile_geometry); - WriteBasicSetting(UISettings::values.microprofile_visible); + + WriteCategory(Settings::Category::UiLayout); qt_config->endGroup(); } @@ -1597,10 +1138,7 @@ void Config::SaveUILayoutValues() { void Config::SaveWebServiceValues() { qt_config->beginGroup(QStringLiteral("WebService")); - WriteBasicSetting(Settings::values.enable_telemetry); - WriteBasicSetting(Settings::values.web_api_url); - WriteBasicSetting(Settings::values.yuzu_username); - WriteBasicSetting(Settings::values.yuzu_token); + WriteCategory(Settings::Category::WebService); qt_config->endGroup(); } @@ -1608,17 +1146,7 @@ void Config::SaveWebServiceValues() { void Config::SaveMultiplayerValues() { qt_config->beginGroup(QStringLiteral("Multiplayer")); - WriteBasicSetting(UISettings::values.multiplayer_nickname); - WriteBasicSetting(UISettings::values.multiplayer_ip); - WriteBasicSetting(UISettings::values.multiplayer_port); - WriteBasicSetting(UISettings::values.multiplayer_room_nickname); - WriteBasicSetting(UISettings::values.multiplayer_room_name); - WriteBasicSetting(UISettings::values.multiplayer_room_port); - WriteBasicSetting(UISettings::values.multiplayer_host_type); - WriteBasicSetting(UISettings::values.multiplayer_port); - WriteBasicSetting(UISettings::values.multiplayer_max_player); - WriteBasicSetting(UISettings::values.multiplayer_game_id); - WriteBasicSetting(UISettings::values.multiplayer_room_description); + WriteCategory(Settings::Category::Multiplayer); // Write ban list qt_config->beginWriteArray(QStringLiteral("username_ban_list")); @@ -1653,27 +1181,6 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) return result; } -template <typename Type, bool ranged> -void Config::ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& setting) { - QString name = QString::fromStdString(setting.GetLabel()); - const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); - setting.SetGlobal(use_global); - if (global || !use_global) { - setting.SetValue(static_cast<QVariant>( - ReadSetting(name, QVariant::fromValue<Type>(setting.GetDefault()))) - .value<Type>()); - } -} - -template <typename Type> -void Config::ReadSettingGlobal(Type& setting, const QString& name, - const QVariant& default_value) const { - const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); - if (global || !use_global) { - setting = ReadSetting(name, default_value).value<Type>(); - } -} - void Config::WriteSetting(const QString& name, const QVariant& value) { qt_config->setValue(name, value); } @@ -1727,3 +1234,66 @@ void Config::ClearControlPlayerValues() { const std::string& Config::GetConfigFilePath() const { return qt_config_loc; } + +static auto FindRelevantList(Settings::Category category) { + auto& map = Settings::values.linkage.by_category; + if (map.contains(category)) { + return Settings::values.linkage.by_category[category]; + } + return UISettings::values.linkage.by_category[category]; +} + +void Config::ReadCategory(Settings::Category category) { + const auto& settings = FindRelevantList(category); + std::for_each(settings.begin(), settings.end(), + [&](const auto& setting) { ReadSettingGeneric(setting); }); +} + +void Config::WriteCategory(Settings::Category category) { + const auto& settings = FindRelevantList(category); + std::for_each(settings.begin(), settings.end(), + [&](const auto& setting) { WriteSettingGeneric(setting); }); +} + +void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) { + if (!setting->Save()) { + return; + } + const QString name = QString::fromStdString(setting->GetLabel()); + const auto default_value = + QVariant::fromValue<QString>(QString::fromStdString(setting->DefaultToString())); + + if (setting->Switchable()) { + const bool use_global = + qt_config->value(name + QStringLiteral("/use_global"), true).value<bool>(); + setting->SetGlobal(use_global); + + if (global || !use_global) { + setting->LoadString(ReadSetting(name, default_value).value<QString>().toStdString()); + } + } else if (global) { + setting->LoadString(ReadSetting(name, default_value).value<QString>().toStdString()); + } +} + +void Config::WriteSettingGeneric(Settings::BasicSetting* const setting) const { + if (!setting->Save()) { + return; + } + const QVariant value = QVariant::fromValue(QString::fromStdString(setting->ToString())); + const QVariant default_value = + QVariant::fromValue(QString::fromStdString(setting->DefaultToString())); + const QString label = QString::fromStdString(setting->GetLabel()); + if (setting->Switchable()) { + if (!global) { + qt_config->setValue(label + QStringLiteral("/use_global"), setting->UsingGlobal()); + } + if (global || !setting->UsingGlobal()) { + qt_config->setValue(label + QStringLiteral("/default"), value == default_value); + qt_config->setValue(label, value); + } + } else if (global) { + qt_config->setValue(label + QStringLiteral("/default"), value == default_value); + qt_config->setValue(label, value); + } +} diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 1211389d27..0ac74c8e72 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -52,7 +52,7 @@ public: static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map; static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map; static const std::map<bool, QString> use_docked_mode_texts_map; - static const std::map<Settings::GPUAccuracy, QString> gpu_accuracy_texts_map; + static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map; static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map; static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map; @@ -74,7 +74,6 @@ private: void ReadKeyboardValues(); void ReadMouseValues(); void ReadTouchscreenValues(); - void ReadMousePanningValues(); void ReadMotionTouchValues(); void ReadHidbusValues(); void ReadIrCameraValues(); @@ -99,13 +98,13 @@ private: void ReadUILayoutValues(); void ReadWebServiceValues(); void ReadMultiplayerValues(); + void ReadNetworkValues(); void SaveValues(); void SavePlayerValue(std::size_t player_index); void SaveDebugValues(); void SaveMouseValues(); void SaveTouchscreenValues(); - void SaveMousePanningValues(); void SaveMotionTouchValues(); void SaveHidbusValues(); void SaveIrCameraValues(); @@ -141,18 +140,6 @@ private: 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 @@ -166,50 +153,20 @@ private: 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); + void ReadCategory(Settings::Category category); + void WriteCategory(Settings::Category category); + void ReadSettingGeneric(Settings::BasicSetting* const setting); + void WriteSettingGeneric(Settings::BasicSetting* const setting) const; - ConfigType type; + const ConfigType type; std::unique_ptr<QSettings> qt_config; std::string qt_config_loc; - bool global; + const bool global; }; // These metatype declarations cannot be in common/settings.h because core is devoid of QT -Q_DECLARE_METATYPE(Settings::CPUAccuracy); -Q_DECLARE_METATYPE(Settings::GPUAccuracy); +Q_DECLARE_METATYPE(Settings::CpuAccuracy); +Q_DECLARE_METATYPE(Settings::GpuAccuracy); Q_DECLARE_METATYPE(Settings::FullscreenMode); Q_DECLARE_METATYPE(Settings::NvdecEmulation); Q_DECLARE_METATYPE(Settings::ResolutionSetup); @@ -218,3 +175,4 @@ Q_DECLARE_METATYPE(Settings::AntiAliasing); Q_DECLARE_METATYPE(Settings::RendererBackend); Q_DECLARE_METATYPE(Settings::ShaderBackend); Q_DECLARE_METATYPE(Settings::AstcRecompression); +Q_DECLARE_METATYPE(Settings::AstcDecodeMode); diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp index ac42cc7fc2..0ed6146a0f 100644 --- a/src/yuzu/configuration/configuration_shared.cpp +++ b/src/yuzu/configuration/configuration_shared.cpp @@ -1,104 +1,19 @@ // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <QCheckBox> -#include <QObject> -#include <QString> -#include "common/settings.h" +#include <memory> +#include <type_traits> +#include <vector> #include "yuzu/configuration/configuration_shared.h" -#include "yuzu/configuration/configure_per_game.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()); - } - } -} +namespace ConfigurationShared { -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); +Tab::Tab(std::shared_ptr<std::vector<Tab*>> group, QWidget* parent) : QWidget(parent) { + if (group != nullptr) { + group->push_back(this); } } -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(); -} +Tab::~Tab() = default; -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); -} - -int ConfigurationShared::GetComboboxIndex(int global_setting_index, const QComboBox* combobox) { - if (Settings::IsConfiguringGlobal()) { - return combobox->currentIndex(); - } - if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - return global_setting_index; - } - return combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET; -} +} // namespace ConfigurationShared diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h index 04c88758c6..31897a6b0a 100644 --- a/src/yuzu/configuration/configuration_shared.h +++ b/src/yuzu/configuration/configuration_shared.h @@ -3,73 +3,25 @@ #pragma once -#include <QCheckBox> -#include <QComboBox> -#include "common/settings.h" +#include <memory> +#include <vector> +#include <QString> +#include <QWidget> +#include <qobjectdefs.h> -namespace ConfigurationShared { - -constexpr int USE_GLOBAL_INDEX = 0; -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 -}; - -// Global-aware apply and set functions - -// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting -void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox, - const CheckState& tracker); -template <typename Type, bool ranged> -void ApplyPerGameSetting(Settings::SwitchableSetting<Type, ranged>* setting, - const QComboBox* combobox) { - if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { - setting->SetValue(static_cast<Type>(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>(combobox->currentIndex() - - ConfigurationShared::USE_GLOBAL_OFFSET)); - } - } -} +class QObject; -// 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); -} - -// (Un)highlights a Qt UI element -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); +namespace ConfigurationShared { -// 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); +class Tab : public QWidget { + Q_OBJECT -// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox -void InsertGlobalItem(QComboBox* combobox, int global_index); +public: + explicit Tab(std::shared_ptr<std::vector<Tab*>> group, QWidget* parent = nullptr); + ~Tab(); -// Returns the correct index of a QComboBox taking into account global configuration -int GetComboboxIndex(int global_setting_index, const QComboBox* combobox); + virtual void ApplyConfiguration() = 0; + virtual void SetConfiguration() = 0; +}; } // namespace ConfigurationShared diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index eb8078467e..573c408014 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -48,11 +48,34 @@ </layout> </item> <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="leftMargin"> + <number>0</number> </property> - </widget> + <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"> + <property name="text"> + <string>Some settings are only available when a game is not running.</string> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> </item> </layout> </widget> diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index fcd6d61a05..9ccfb24352 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -1,87 +1,112 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <map> #include <memory> +#include <vector> +#include <QComboBox> #include "audio_core/sink/sink.h" #include "audio_core/sink/sink_details.h" +#include "common/common_types.h" #include "common/settings.h" +#include "common/settings_common.h" #include "core/core.h" #include "ui_configure_audio.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_audio.h" +#include "yuzu/configuration/shared_translation.h" +#include "yuzu/configuration/shared_widget.h" #include "yuzu/uisettings.h" -ConfigureAudio::ConfigureAudio(const Core::System& system_, QWidget* parent) - : QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()), system{system_} { +ConfigureAudio::ConfigureAudio(const Core::System& system_, + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_, + const ConfigurationShared::Builder& builder, QWidget* parent) + : Tab(group_, parent), ui(std::make_unique<Ui::ConfigureAudio>()), system{system_} { ui->setupUi(this); + Setup(builder); - InitializeAudioSinkComboBox(); + SetConfiguration(); +} - connect(ui->volume_slider, &QSlider::valueChanged, this, - &ConfigureAudio::SetVolumeIndicatorText); - connect(ui->sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, - &ConfigureAudio::UpdateAudioDevices); +ConfigureAudio::~ConfigureAudio() = default; - ui->volume_label->setVisible(Settings::IsConfiguringGlobal()); - ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal()); +void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) { + auto& layout = *ui->audio_widget->layout(); - SetupPerGameUI(); + std::vector<Settings::BasicSetting*> settings; - SetConfiguration(); + std::map<u32, QWidget*> hold; - const bool is_powered_on = system_.IsPoweredOn(); - ui->sink_combo_box->setEnabled(!is_powered_on); - ui->output_combo_box->setEnabled(!is_powered_on); - ui->input_combo_box->setEnabled(!is_powered_on); -} + auto push = [&](Settings::Category category) { + for (auto* setting : Settings::values.linkage.by_category[category]) { + settings.push_back(setting); + } + }; -ConfigureAudio::~ConfigureAudio() = default; + push(Settings::Category::Audio); + push(Settings::Category::SystemAudio); -void ConfigureAudio::SetConfiguration() { - SetOutputSinkFromSinkID(); + for (auto* setting : settings) { + auto* widget = builder.BuildWidget(setting, apply_funcs); - // The device list cannot be pre-populated (nor listed) until the output sink is known. - UpdateAudioDevices(ui->sink_combo_box->currentIndex()); + if (widget == nullptr) { + continue; + } + if (!widget->Valid()) { + widget->deleteLater(); + continue; + } - SetAudioDevicesFromDeviceID(); + hold.emplace(std::pair{setting->Id(), widget}); + + if (setting->Id() == Settings::values.sink_id.Id()) { + // TODO (lat9nq): Let the system manage sink_id + sink_combo_box = widget->combobox; + InitializeAudioSinkComboBox(); + + connect(sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, + &ConfigureAudio::UpdateAudioDevices); + } else if (setting->Id() == Settings::values.audio_output_device_id.Id()) { + // Keep track of output (and input) device comboboxes to populate them with system + // devices, which are determined at run time + output_device_combo_box = widget->combobox; + } else if (setting->Id() == Settings::values.audio_input_device_id.Id()) { + input_device_combo_box = widget->combobox; + } + } - const auto volume_value = static_cast<int>(Settings::values.volume.GetValue()); - ui->volume_slider->setValue(volume_value); - ui->toggle_background_mute->setChecked(UISettings::values.mute_when_in_background.GetValue()); + for (const auto& [id, widget] : hold) { + layout.addWidget(widget); + } +} +void ConfigureAudio::SetConfiguration() { if (!Settings::IsConfiguringGlobal()) { - if (Settings::values.volume.UsingGlobal()) { - ui->volume_combo_box->setCurrentIndex(0); - ui->volume_slider->setEnabled(false); - } else { - ui->volume_combo_box->setCurrentIndex(1); - ui->volume_slider->setEnabled(true); - } - ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index); - ConfigurationShared::SetHighlight(ui->mode_label, - !Settings::values.sound_index.UsingGlobal()); - ConfigurationShared::SetHighlight(ui->volume_layout, - !Settings::values.volume.UsingGlobal()); - } else { - ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue()); + return; } - SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); + + SetOutputSinkFromSinkID(); + + // The device list cannot be pre-populated (nor listed) until the output sink is known. + UpdateAudioDevices(sink_combo_box->currentIndex()); + + SetAudioDevicesFromDeviceID(); } void ConfigureAudio::SetOutputSinkFromSinkID() { - [[maybe_unused]] const QSignalBlocker blocker(ui->sink_combo_box); + [[maybe_unused]] const QSignalBlocker blocker(sink_combo_box); int new_sink_index = 0; - const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue()); - for (int index = 0; index < ui->sink_combo_box->count(); index++) { - if (ui->sink_combo_box->itemText(index) == sink_id) { + const QString sink_id = QString::fromStdString(Settings::values.sink_id.ToString()); + for (int index = 0; index < sink_combo_box->count(); index++) { + if (sink_combo_box->itemText(index) == sink_id) { new_sink_index = index; break; } } - ui->sink_combo_box->setCurrentIndex(new_sink_index); + sink_combo_box->setCurrentIndex(new_sink_index); } void ConfigureAudio::SetAudioDevicesFromDeviceID() { @@ -89,57 +114,42 @@ void ConfigureAudio::SetAudioDevicesFromDeviceID() { const QString output_device_id = QString::fromStdString(Settings::values.audio_output_device_id.GetValue()); - for (int index = 0; index < ui->output_combo_box->count(); index++) { - if (ui->output_combo_box->itemText(index) == output_device_id) { + for (int index = 0; index < output_device_combo_box->count(); index++) { + if (output_device_combo_box->itemText(index) == output_device_id) { new_device_index = index; break; } } - ui->output_combo_box->setCurrentIndex(new_device_index); + output_device_combo_box->setCurrentIndex(new_device_index); new_device_index = -1; const QString input_device_id = QString::fromStdString(Settings::values.audio_input_device_id.GetValue()); - for (int index = 0; index < ui->input_combo_box->count(); index++) { - if (ui->input_combo_box->itemText(index) == input_device_id) { + for (int index = 0; index < input_device_combo_box->count(); index++) { + if (input_device_combo_box->itemText(index) == input_device_id) { new_device_index = index; break; } } - ui->input_combo_box->setCurrentIndex(new_device_index); -} - -void ConfigureAudio::SetVolumeIndicatorText(int percentage) { - ui->volume_indicator->setText(tr("%1%", "Volume percentage (e.g. 50%)").arg(percentage)); + input_device_combo_box->setCurrentIndex(new_device_index); } void ConfigureAudio::ApplyConfiguration() { - ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound); + const bool is_powered_on = system.IsPoweredOn(); + for (const auto& apply_func : apply_funcs) { + apply_func(is_powered_on); + } if (Settings::IsConfiguringGlobal()) { - Settings::values.sink_id = - ui->sink_combo_box->itemText(ui->sink_combo_box->currentIndex()).toStdString(); + Settings::values.sink_id.LoadString( + sink_combo_box->itemText(sink_combo_box->currentIndex()).toStdString()); Settings::values.audio_output_device_id.SetValue( - ui->output_combo_box->itemText(ui->output_combo_box->currentIndex()).toStdString()); + output_device_combo_box->itemText(output_device_combo_box->currentIndex()) + .toStdString()); Settings::values.audio_input_device_id.SetValue( - ui->input_combo_box->itemText(ui->input_combo_box->currentIndex()).toStdString()); - UISettings::values.mute_when_in_background = ui->toggle_background_mute->isChecked(); - - // Guard if during game and set to game-specific value - if (Settings::values.volume.UsingGlobal()) { - const auto volume = static_cast<u8>(ui->volume_slider->value()); - Settings::values.volume.SetValue(volume); - } - } else { - if (ui->volume_combo_box->currentIndex() == 0) { - Settings::values.volume.SetGlobal(true); - } else { - Settings::values.volume.SetGlobal(false); - const auto volume = static_cast<u8>(ui->volume_slider->value()); - Settings::values.volume.SetValue(volume); - } + input_device_combo_box->itemText(input_device_combo_box->currentIndex()).toStdString()); } } @@ -152,54 +162,31 @@ void ConfigureAudio::changeEvent(QEvent* event) { } void ConfigureAudio::UpdateAudioDevices(int sink_index) { - ui->output_combo_box->clear(); - ui->output_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); + output_device_combo_box->clear(); + output_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); - const std::string sink_id = ui->sink_combo_box->itemText(sink_index).toStdString(); + const auto sink_id = + Settings::ToEnum<Settings::AudioEngine>(sink_combo_box->itemText(sink_index).toStdString()); for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, false)) { - ui->output_combo_box->addItem(QString::fromStdString(device)); + output_device_combo_box->addItem(QString::fromStdString(device)); } - ui->input_combo_box->clear(); - ui->input_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); + input_device_combo_box->clear(); + input_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, true)) { - ui->input_combo_box->addItem(QString::fromStdString(device)); + input_device_combo_box->addItem(QString::fromStdString(device)); } } void ConfigureAudio::InitializeAudioSinkComboBox() { - ui->sink_combo_box->clear(); - ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); + sink_combo_box->clear(); + sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); for (const auto& id : AudioCore::Sink::GetSinkIDs()) { - ui->sink_combo_box->addItem(QString::fromUtf8(id.data(), static_cast<s32>(id.length()))); + sink_combo_box->addItem(QString::fromStdString(Settings::CanonicalizeEnum(id))); } } void ConfigureAudio::RetranslateUI() { ui->retranslateUi(this); - SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); -} - -void ConfigureAudio::SetupPerGameUI() { - if (Settings::IsConfiguringGlobal()) { - ui->combo_sound->setEnabled(Settings::values.sound_index.UsingGlobal()); - ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal()); - return; - } - - ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->mode_label, - Settings::values.sound_index.GetValue(true)); - - 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); - }); - - ui->sink_combo_box->setVisible(false); - ui->sink_label->setVisible(false); - ui->output_combo_box->setVisible(false); - ui->output_label->setVisible(false); - ui->input_combo_box->setVisible(false); - ui->input_label->setVisible(false); } diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h index 0d03aae1df..79538e81c6 100644 --- a/src/yuzu/configuration/configure_audio.h +++ b/src/yuzu/configuration/configure_audio.h @@ -3,30 +3,35 @@ #pragma once +#include <functional> #include <memory> +#include <vector> #include <QWidget> +#include "yuzu/configuration/configuration_shared.h" + +class QComboBox; namespace Core { class System; } -namespace ConfigurationShared { -enum class CheckState; -} - namespace Ui { class ConfigureAudio; } -class ConfigureAudio : public QWidget { - Q_OBJECT +namespace ConfigurationShared { +class Builder; +} +class ConfigureAudio : public ConfigurationShared::Tab { public: - explicit ConfigureAudio(const Core::System& system_, QWidget* parent = nullptr); + explicit ConfigureAudio(const Core::System& system_, + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group, + const ConfigurationShared::Builder& builder, QWidget* parent = nullptr); ~ConfigureAudio() override; - void ApplyConfiguration(); - void SetConfiguration(); + void ApplyConfiguration() override; + void SetConfiguration() override; private: void changeEvent(QEvent* event) override; @@ -39,11 +44,16 @@ private: void SetOutputSinkFromSinkID(); void SetAudioDevicesFromDeviceID(); - void SetVolumeIndicatorText(int percentage); - void SetupPerGameUI(); + void Setup(const ConfigurationShared::Builder& builder); std::unique_ptr<Ui::ConfigureAudio> ui; const Core::System& system; + + std::vector<std::function<void(bool)>> apply_funcs{}; + + QComboBox* sink_combo_box; + QComboBox* output_device_combo_box; + QComboBox* input_device_combo_box; }; diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui index 4128c83ad4..1181aeb007 100644 --- a/src/yuzu/configuration/configure_audio.ui +++ b/src/yuzu/configuration/configure_audio.ui @@ -21,80 +21,14 @@ </property> <layout class="QVBoxLayout"> <item> - <layout class="QHBoxLayout" name="engine_layout"> - <item> - <widget class="QLabel" name="sink_label"> - <property name="text"> - <string>Output Engine:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="sink_combo_box"/> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="output_layout"> - <item> - <widget class="QLabel" name="output_label"> - <property name="text"> - <string>Output Device:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="output_combo_box"/> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="input_layout"> - <item> - <widget class="QLabel" name="input_label"> - <property name="text"> - <string>Input Device:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="input_combo_box"/> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="mode_layout"> - <item> - <widget class="QLabel" name="mode_label"> - <property name="text"> - <string>Sound Output Mode:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="combo_sound"> - <item> - <property name="text"> - <string>Mono</string> - </property> - </item> - <item> - <property name="text"> - <string>Stereo</string> - </property> - </item> - <item> - <property name="text"> - <string>Surround</string> - </property> - </item> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QWidget" name="volume_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_2"> + <widget class="QWidget" name="audio_widget" native="true"> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>16777213</height> + </size> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> <property name="leftMargin"> <number>0</number> </property> @@ -107,89 +41,9 @@ <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>200</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> - <item> - <layout class="QHBoxLayout" name="mute_layout"> - <item> - <widget class="QCheckBox" name="toggle_background_mute"> - <property name="text"> - <string>Mute audio when in background</string> - </property> - </widget> - </item> - </layout> - </item> </layout> </widget> </item> diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp index 3d69fb03ff..a513599038 100644 --- a/src/yuzu/configuration/configure_cpu.cpp +++ b/src/yuzu/configuration/configure_cpu.cpp @@ -1,88 +1,92 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <memory> +#include <typeinfo> +#include <vector> +#include <QComboBox> #include "common/common_types.h" #include "common/settings.h" +#include "common/settings_enums.h" +#include "configuration/shared_widget.h" #include "core/core.h" #include "ui_configure_cpu.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_cpu.h" -ConfigureCpu::ConfigureCpu(const Core::System& system_, QWidget* parent) - : QWidget(parent), ui{std::make_unique<Ui::ConfigureCpu>()}, system{system_} { +ConfigureCpu::ConfigureCpu(const Core::System& system_, + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_, + const ConfigurationShared::Builder& builder, QWidget* parent) + : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureCpu>()}, system{system_}, + combobox_translations(builder.ComboboxTranslations()) { ui->setupUi(this); - SetupPerGameUI(); + Setup(builder); SetConfiguration(); - connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this, + connect(accuracy_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ConfigureCpu::UpdateGroup); } ConfigureCpu::~ConfigureCpu() = default; -void ConfigureCpu::SetConfiguration() { - const bool runtime_lock = !system.IsPoweredOn(); - - ui->accuracy->setEnabled(runtime_lock); - ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock); - ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); - ui->cpuopt_unsafe_ignore_standard_fpcr->setEnabled(runtime_lock); - ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); - ui->cpuopt_unsafe_fastmem_check->setEnabled(runtime_lock); - ui->cpuopt_unsafe_ignore_global_monitor->setEnabled(runtime_lock); - - ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()); - ui->cpuopt_unsafe_reduce_fp_error->setChecked( - Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()); - ui->cpuopt_unsafe_ignore_standard_fpcr->setChecked( - Settings::values.cpuopt_unsafe_ignore_standard_fpcr.GetValue()); - ui->cpuopt_unsafe_inaccurate_nan->setChecked( - Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()); - ui->cpuopt_unsafe_fastmem_check->setChecked( - Settings::values.cpuopt_unsafe_fastmem_check.GetValue()); - ui->cpuopt_unsafe_ignore_global_monitor->setChecked( - Settings::values.cpuopt_unsafe_ignore_global_monitor.GetValue()); - - if (Settings::IsConfiguringGlobal()) { - ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy.GetValue())); - } else { - ConfigurationShared::SetPerGameSetting(ui->accuracy, &Settings::values.cpu_accuracy); - ConfigurationShared::SetHighlight(ui->widget_accuracy, - !Settings::values.cpu_accuracy.UsingGlobal()); +void ConfigureCpu::SetConfiguration() {} +void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) { + auto* accuracy_layout = ui->widget_accuracy->layout(); + auto* unsafe_layout = ui->unsafe_widget->layout(); + std::map<u32, QWidget*> unsafe_hold{}; + + std::vector<Settings::BasicSetting*> settings; + const auto push = [&](Settings::Category category) { + for (const auto setting : Settings::values.linkage.by_category[category]) { + settings.push_back(setting); + } + }; + + push(Settings::Category::Cpu); + push(Settings::Category::CpuUnsafe); + + for (const auto setting : settings) { + auto* widget = builder.BuildWidget(setting, apply_funcs); + + if (widget == nullptr) { + continue; + } + if (!widget->Valid()) { + widget->deleteLater(); + continue; + } + + if (setting->Id() == Settings::values.cpu_accuracy.Id()) { + // Keep track of cpu_accuracy combobox to display/hide the unsafe settings + accuracy_layout->addWidget(widget); + accuracy_combobox = widget->combobox; + } else { + // Presently, all other settings here are unsafe checkboxes + unsafe_hold.insert({setting->Id(), widget}); + } } - UpdateGroup(ui->accuracy->currentIndex()); + + for (const auto& [label, widget] : unsafe_hold) { + unsafe_layout->addWidget(widget); + } + + UpdateGroup(accuracy_combobox->currentIndex()); } void ConfigureCpu::UpdateGroup(int index) { - if (!Settings::IsConfiguringGlobal()) { - index -= ConfigurationShared::USE_GLOBAL_OFFSET; - } - const auto accuracy = static_cast<Settings::CPUAccuracy>(index); - ui->unsafe_group->setVisible(accuracy == Settings::CPUAccuracy::Unsafe); + const auto accuracy = static_cast<Settings::CpuAccuracy>( + combobox_translations.at(Settings::EnumMetadata<Settings::CpuAccuracy>::Index())[index] + .first); + ui->unsafe_group->setVisible(accuracy == Settings::CpuAccuracy::Unsafe); } void ConfigureCpu::ApplyConfiguration() { - ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpu_accuracy, ui->accuracy); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_unfuse_fma, - ui->cpuopt_unsafe_unfuse_fma, - cpuopt_unsafe_unfuse_fma); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_reduce_fp_error, - ui->cpuopt_unsafe_reduce_fp_error, - cpuopt_unsafe_reduce_fp_error); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_ignore_standard_fpcr, - ui->cpuopt_unsafe_ignore_standard_fpcr, - cpuopt_unsafe_ignore_standard_fpcr); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_inaccurate_nan, - ui->cpuopt_unsafe_inaccurate_nan, - cpuopt_unsafe_inaccurate_nan); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check, - ui->cpuopt_unsafe_fastmem_check, - cpuopt_unsafe_fastmem_check); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_ignore_global_monitor, - ui->cpuopt_unsafe_ignore_global_monitor, - cpuopt_unsafe_ignore_global_monitor); + const bool is_powered_on = system.IsPoweredOn(); + for (const auto& apply_func : apply_funcs) { + apply_func(is_powered_on); + } } void ConfigureCpu::changeEvent(QEvent* event) { @@ -96,32 +100,3 @@ void ConfigureCpu::changeEvent(QEvent* event) { void ConfigureCpu::RetranslateUI() { ui->retranslateUi(this); } - -void ConfigureCpu::SetupPerGameUI() { - if (Settings::IsConfiguringGlobal()) { - return; - } - - ConfigurationShared::SetColoredComboBox( - ui->accuracy, ui->widget_accuracy, - static_cast<u32>(Settings::values.cpu_accuracy.GetValue(true))); - - ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_unfuse_fma, - Settings::values.cpuopt_unsafe_unfuse_fma, - cpuopt_unsafe_unfuse_fma); - ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_reduce_fp_error, - Settings::values.cpuopt_unsafe_reduce_fp_error, - cpuopt_unsafe_reduce_fp_error); - ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_ignore_standard_fpcr, - Settings::values.cpuopt_unsafe_ignore_standard_fpcr, - cpuopt_unsafe_ignore_standard_fpcr); - ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_inaccurate_nan, - Settings::values.cpuopt_unsafe_inaccurate_nan, - cpuopt_unsafe_inaccurate_nan); - ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_fastmem_check, - Settings::values.cpuopt_unsafe_fastmem_check, - cpuopt_unsafe_fastmem_check); - ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_ignore_global_monitor, - Settings::values.cpuopt_unsafe_ignore_global_monitor, - cpuopt_unsafe_ignore_global_monitor); -} diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h index 86d928ca3b..61a6de7aa7 100644 --- a/src/yuzu/configuration/configure_cpu.h +++ b/src/yuzu/configuration/configure_cpu.h @@ -4,29 +4,34 @@ #pragma once #include <memory> +#include <vector> #include <QWidget> +#include "yuzu/configuration/configuration_shared.h" +#include "yuzu/configuration/shared_translation.h" + +class QComboBox; namespace Core { class System; } -namespace ConfigurationShared { -enum class CheckState; -} - namespace Ui { class ConfigureCpu; } -class ConfigureCpu : public QWidget { - Q_OBJECT +namespace ConfigurationShared { +class Builder; +} +class ConfigureCpu : public ConfigurationShared::Tab { public: - explicit ConfigureCpu(const Core::System& system_, QWidget* parent = nullptr); + explicit ConfigureCpu(const Core::System& system_, + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group, + const ConfigurationShared::Builder& builder, QWidget* parent = nullptr); ~ConfigureCpu() override; - void ApplyConfiguration(); - void SetConfiguration(); + void ApplyConfiguration() override; + void SetConfiguration() override; private: void changeEvent(QEvent* event) override; @@ -34,16 +39,14 @@ private: void UpdateGroup(int index); - void SetupPerGameUI(); + void Setup(const ConfigurationShared::Builder& builder); std::unique_ptr<Ui::ConfigureCpu> ui; - ConfigurationShared::CheckState cpuopt_unsafe_unfuse_fma; - ConfigurationShared::CheckState cpuopt_unsafe_reduce_fp_error; - ConfigurationShared::CheckState cpuopt_unsafe_ignore_standard_fpcr; - ConfigurationShared::CheckState cpuopt_unsafe_inaccurate_nan; - ConfigurationShared::CheckState cpuopt_unsafe_fastmem_check; - ConfigurationShared::CheckState cpuopt_unsafe_ignore_global_monitor; - const Core::System& system; + + const ConfigurationShared::ComboboxTranslationMap& combobox_translations; + std::vector<std::function<void(bool)>> apply_funcs{}; + + QComboBox* accuracy_combobox; }; diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui index 8ae569ee67..f734e842e8 100644 --- a/src/yuzu/configuration/configure_cpu.ui +++ b/src/yuzu/configuration/configure_cpu.ui @@ -16,9 +16,12 @@ <property name="accessibleName"> <string>CPU</string> </property> - <layout class="QVBoxLayout"> + <layout class="QVBoxLayout" name="vboxlayout_2" stretch="0"> <item> - <layout class="QVBoxLayout"> + <layout class="QVBoxLayout" name="vboxlayout"> + <property name="bottomMargin"> + <number>0</number> + </property> <item> <widget class="QGroupBox" name="groupBox"> <property name="title"> @@ -27,38 +30,19 @@ <layout class="QVBoxLayout"> <item> <widget class="QWidget" name="widget_accuracy" native="true"> - <layout class="QHBoxLayout" name="layout_accuracy"> - <item> - <widget class="QLabel" name="label_accuracy"> - <property name="text"> - <string>Accuracy:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="accuracy"> - <item> - <property name="text"> - <string>Auto</string> - </property> - </item> - <item> - <property name="text"> - <string>Accurate</string> - </property> - </item> - <item> - <property name="text"> - <string>Unsafe</string> - </property> - </item> - <item> - <property name="text"> - <string>Paranoid (disables most optimizations)</string> - </property> - </item> - </widget> - </item> + <layout class="QVBoxLayout" name="verticalLayout"> + <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> </layout> </widget> </item> @@ -75,10 +59,6 @@ </layout> </widget> </item> - </layout> - </item> - <item> - <layout class="QVBoxLayout"> <item> <widget class="QGroupBox" name="unsafe_group"> <property name="title"> @@ -96,105 +76,44 @@ </widget> </item> <item> - <widget class="QCheckBox" name="cpuopt_unsafe_unfuse_fma"> - <property name="toolTip"> - <string> - <div>This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.</div> - </string> - </property> - <property name="text"> - <string>Unfuse FMA (improve performance on CPUs without FMA)</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="cpuopt_unsafe_reduce_fp_error"> - <property name="toolTip"> - <string> - <div>This option improves the speed of some approximate floating-point functions by using less accurate native approximations.</div> - </string> - </property> - <property name="text"> - <string>Faster FRSQRTE and FRECPE</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="cpuopt_unsafe_ignore_standard_fpcr"> - <property name="toolTip"> - <string> - <div>This option improves the speed of 32 bits ASIMD floating-point functions by running with incorrect rounding modes.</div> - </string> - </property> - <property name="text"> - <string>Faster ASIMD instructions (32 bits only)</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan"> - <property name="toolTip"> - <string> - <div>This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.</div> - </string> - </property> - <property name="text"> - <string>Inaccurate NaN handling</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="cpuopt_unsafe_fastmem_check"> - <property name="toolTip"> - <string> - <div>This option improves speed by eliminating a safety check before every memory read/write in guest. Disabling it may allow a game to read/write the emulator's memory.</div> - </string> - </property> - <property name="text"> - <string>Disable address space checks</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="cpuopt_unsafe_ignore_global_monitor"> - <property name="toolTip"> - <string> - <div>This option improves speed by relying only on the semantics of cmpxchg to ensure safety of exclusive access instructions. Please note this may result in deadlocks and other race conditions.</div> - </string> - </property> - <property name="text"> - <string>Ignore global monitor</string> - </property> + <widget class="QWidget" name="unsafe_widget" native="true"> + <layout class="QVBoxLayout" name="unsafe_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> + </layout> </widget> </item> </layout> </widget> </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </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> - <item> - <widget class="QLabel" name="label_disable_info"> - <property name="text"> - <string>CPU settings are available only when game is not running.</string> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> </layout> </widget> <resources/> diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 15acefe332..97c7d9022e 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -2,360 +2,549 @@ <ui version="4.0"> <class>ConfigureDebug</class> <widget class="QScrollArea" name="ConfigureDebug"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>831</width> + <height>760</height> + </rect> + </property> <property name="widgetResizable"> <bool>true</bool> </property> - <widget class="QWidget"> - <layout class="QVBoxLayout" name="verticalLayout_1"> - <item> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <widget class="QGroupBox" name="groupBox"> - <property name="title"> - <string>Debugger</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_11"> - <item> - <widget class="QCheckBox" name="toggle_gdbstub"> - <property name="text"> - <string>Enable GDB Stub</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <widget class="QWidget" name="widget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>829</width> + <height>758</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout_1"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="topMargin"> + <number>0</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Debugger</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QWidget" name="debug_widget" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> + <property name="leftMargin"> + <number>0</number> </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_11"> - <property name="text"> - <string>Port:</string> + <property name="topMargin"> + <number>0</number> </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="gdbport_spinbox"> - <property name="minimum"> - <number>1024</number> + <property name="rightMargin"> + <number>0</number> </property> - <property name="maximum"> - <number>65535</number> + <property name="bottomMargin"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QGroupBox" name="groupBox_2"> - <property name="title"> - <string>Logging</string> - </property> - <layout class="QGridLayout" name="gridLayout_1"> - <item row="0" column="0" colspan="2"> - <layout class="QHBoxLayout" name="horizontalLayout_1"> - <item> - <widget class="QLabel" name="label_1"> - <property name="text"> - <string>Global Log Filter</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="log_filter_edit"/> - </item> - </layout> - </item> - <item row="1" column="0"> - <widget class="QCheckBox" name="toggle_console"> - <property name="text"> - <string>Show Log in Console</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QPushButton" name="open_log_button"> - <property name="text"> - <string>Open Log Location</string> - </property> + <item> + <widget class="QCheckBox" name="toggle_gdbstub"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Enable GDB Stub</string> + </property> + </widget> + </item> + <item> + <widget class="QWidget" name="horizontalWidget_3" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <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_11"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Port:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="gdbport_spinbox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimum"> + <number>1024</number> + </property> + <property name="maximum"> + <number>65535</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> </widget> </item> - <item row="2" column="0"> - <widget class="QCheckBox" name="extended_logging"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>When checked, the max size of the log increases from 100 MB to 1 GB</string> - </property> - <property name="text"> - <string>Enable Extended Logging**</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="groupBox_3"> - <property name="title"> - <string>Homebrew</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_5"> <item> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <item> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Arguments String</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="homebrew_args_edit"/> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="groupBox_4"> - <property name="title"> - <string>Graphics</string> - </property> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="0" column="0"> - <widget class="QCheckBox" name="enable_graphics_debugging"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>When checked, the graphics API enters a slower debugging mode</string> - </property> - <property name="text"> - <string>Enable Graphics Debugging</string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QCheckBox" name="enable_nsight_aftermath"> - <property name="toolTip"> - <string>When checked, it enables Nsight Aftermath crash dumps</string> - </property> - <property name="text"> - <string>Enable Nsight Aftermath</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QCheckBox" name="dump_shaders"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string> - </property> - <property name="text"> - <string>Dump Game Shaders</string> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="QCheckBox" name="dump_macros"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>When checked, it will dump all the macro programs of the GPU</string> - </property> - <property name="text"> - <string>Dump Maxwell Macros</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QCheckBox" name="disable_macro_jit"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</string> - </property> - <property name="text"> - <string>Disable Macro JIT</string> - </property> - </widget> - </item> - <item row="0" column="2"> - <widget class="QCheckBox" name="disable_macro_hle"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string> - </property> - <property name="text"> - <string>Disable Macro HLE</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QCheckBox" name="enable_shader_feedback"> - <property name="toolTip"> - <string>When checked, yuzu will log statistics about the compiled pipeline cache</string> - </property> - <property name="text"> - <string>Enable Shader Feedback</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QCheckBox" name="disable_loop_safety_checks"> - <property name="toolTip"> - <string>When checked, it executes shaders without loop logic changes</string> - </property> - <property name="text"> - <string>Disable Loop safety checks</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="groupBox_5"> - <property name="title"> - <string>Debugging</string> - </property> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="2" column="0"> - <widget class="QCheckBox" name="reporting_services"> - <property name="text"> - <string>Enable Verbose Reporting Services**</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QCheckBox" name="fs_access_log"> - <property name="text"> - <string>Enable FS Access Log</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QCheckBox" name="dump_audio_commands"> - <property name="toolTip"> - <string>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</string> - </property> - <property name="text"> - <string>Dump Audio Commands To Console**</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QCheckBox" name="create_crash_dumps"> - <property name="text"> - <string>Create Minidump After Crash</string> - </property> + <widget class="QGroupBox" name="groupBox_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Logging</string> + </property> + <layout class="QGridLayout" name="gridLayout_1"> + <item row="1" column="1"> + <widget class="QPushButton" name="open_log_button"> + <property name="text"> + <string>Open Log Location</string> + </property> + </widget> + </item> + <item row="0" column="0" colspan="2"> + <widget class="QWidget" name="logging_widget" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_1"> + <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_1"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Global Log Filter</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="log_filter_edit"/> + </item> + </layout> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="extended_logging"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip"> + <string>When checked, the max size of the log increases from 100 MB to 1 GB</string> + </property> + <property name="text"> + <string>Enable Extended Logging**</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="toggle_console"> + <property name="text"> + <string>Show Log in Console</string> + </property> + </widget> + </item> + </layout> </widget> </item> </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="groupBox_6"> - <property name="title"> - <string>Advanced</string> - </property> - <layout class="QGridLayout" name="gridLayout_4"> - <item row="0" column="0"> - <widget class="QCheckBox" name="quest_flag"> - <property name="text"> - <string>Kiosk (Quest) Mode</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QCheckBox" name="enable_cpu_debugging"> - <property name="text"> - <string>Enable CPU Debugging</string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QCheckBox" name="use_debug_asserts"> - <property name="text"> - <string>Enable Debug Asserts</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QCheckBox" name="use_auto_stub"> - <property name="text"> - <string>Enable Auto-Stub**</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QCheckBox" name="enable_all_controllers"> - <property name="text"> - <string>Enable All Controller Types</string> - </property> + </item> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Homebrew</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Arguments String</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="homebrew_args_edit"/> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QGroupBox" name="groupBox_4"> + <property name="title"> + <string>Graphics</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="3" column="0"> + <widget class="QCheckBox" name="disable_loop_safety_checks"> + <property name="toolTip"> + <string>When checked, it executes shaders without loop logic changes</string> + </property> + <property name="text"> + <string>Disable Loop safety checks</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="dump_shaders"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip"> + <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string> + </property> + <property name="text"> + <string>Dump Game Shaders</string> + </property> + </widget> + </item> + <item row="7" column="0"> + <widget class="QCheckBox" name="disable_macro_hle"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip"> + <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string> + </property> + <property name="text"> + <string>Disable Macro HLE</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QCheckBox" name="disable_macro_jit"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip"> + <string>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</string> + </property> + <property name="text"> + <string>Disable Macro JIT</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QCheckBox" name="enable_graphics_debugging"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip"> + <string>When checked, the graphics API enters a slower debugging mode</string> + </property> + <property name="text"> + <string>Enable Graphics Debugging</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QCheckBox" name="dump_macros"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip"> + <string>When checked, it will dump all the macro programs of the GPU</string> + </property> + <property name="text"> + <string>Dump Maxwell Macros</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="enable_shader_feedback"> + <property name="toolTip"> + <string>When checked, yuzu will log statistics about the compiled pipeline cache</string> + </property> + <property name="text"> + <string>Enable Shader Feedback</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="enable_nsight_aftermath"> + <property name="toolTip"> + <string>When checked, it enables Nsight Aftermath crash dumps</string> + </property> + <property name="text"> + <string>Enable Nsight Aftermath</string> + </property> + </widget> + </item> + <item row="8" column="0"> + <spacer name="verticalSpacer_5"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> </widget> </item> - <item row="2" column="1"> - <widget class="QCheckBox" name="disable_web_applet"> - <property name="text"> - <string>Disable Web Applet</string> - </property> + <item> + <widget class="QGroupBox" name="groupBox_6"> + <property name="title"> + <string>Advanced</string> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="3" column="0"> + <widget class="QCheckBox" name="perform_vulkan_check"> + <property name="toolTip"> + <string>Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu.</string> + </property> + <property name="text"> + <string>Perform Startup Vulkan Check</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="disable_web_applet"> + <property name="text"> + <string>Disable Web Applet</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QCheckBox" name="enable_all_controllers"> + <property name="text"> + <string>Enable All Controller Types</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QCheckBox" name="use_auto_stub"> + <property name="text"> + <string>Enable Auto-Stub**</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QCheckBox" name="quest_flag"> + <property name="text"> + <string>Kiosk (Quest) Mode</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="enable_cpu_debugging"> + <property name="text"> + <string>Enable CPU Debugging</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="use_debug_asserts"> + <property name="text"> + <string>Enable Debug Asserts</string> + </property> + </widget> + </item> + <item row="7" column="0"> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> </widget> </item> - <item row="3" column="0"> - <widget class="QCheckBox" name="perform_vulkan_check"> - <property name="toolTip"> - <string>Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu.</string> - </property> - <property name="text"> - <string>Perform Startup Vulkan Check</string> - </property> + <item> + <widget class="QGroupBox" name="groupBox_5"> + <property name="title"> + <string>Debugging</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QCheckBox" name="fs_access_log"> + <property name="text"> + <string>Enable FS Access Log</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="create_crash_dumps"> + <property name="text"> + <string>Create Minidump After Crash</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QCheckBox" name="dump_audio_commands"> + <property name="toolTip"> + <string>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</string> + </property> + <property name="text"> + <string>Dump Audio Commands To Console**</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="reporting_services"> + <property name="text"> + <string>Enable Verbose Reporting Services**</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> </widget> </item> </layout> - </widget> - </item> - <item> - <widget class="QLabel" name="label_5"> - <property name="font"> - <font> - <italic>true</italic> - </font> - </property> - <property name="text"> - <string>**This will be reset automatically when yuzu closes.</string> - </property> - <property name="indent"> - <number>20</number> - </property> - </widget> - </item> - </layout> - </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <italic>true</italic> + </font> + </property> + <property name="text"> + <string>**This will be reset automatically when yuzu closes.</string> + </property> + <property name="indent"> + <number>20</number> + </property> + </widget> + </item> + </layout> + </widget> </widget> <tabstops> <tabstop>log_filter_edit</tabstop> @@ -366,14 +555,11 @@ <tabstop>enable_graphics_debugging</tabstop> <tabstop>enable_shader_feedback</tabstop> <tabstop>enable_nsight_aftermath</tabstop> - <tabstop>disable_macro_jit</tabstop> - <tabstop>disable_loop_safety_checks</tabstop> <tabstop>fs_access_log</tabstop> <tabstop>reporting_services</tabstop> <tabstop>quest_flag</tabstop> <tabstop>enable_cpu_debugging</tabstop> <tabstop>use_debug_asserts</tabstop> - <tabstop>use_auto_stub</tabstop> </tabstops> <resources/> <connections/> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index bdf83ebfe3..3c6bb3eb1a 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -32,21 +32,23 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, std::vector<VkDeviceInfo::Record>& vk_device_records, Core::System& system_, bool enable_web_config) : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, - registry(registry_), system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_, - this)}, - cpu_tab{std::make_unique<ConfigureCpu>(system_, this)}, + registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>( + this, !system_.IsPoweredOn())}, + audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)}, + cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)}, debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)}, filesystem_tab{std::make_unique<ConfigureFilesystem>(this)}, - general_tab{std::make_unique<ConfigureGeneral>(system_, this)}, - graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)}, + general_tab{std::make_unique<ConfigureGeneral>(system_, nullptr, *builder, this)}, + graphics_advanced_tab{ + std::make_unique<ConfigureGraphicsAdvanced>(system_, nullptr, *builder, this)}, graphics_tab{std::make_unique<ConfigureGraphics>( system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, - this)}, + nullptr, *builder, this)}, hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)}, input_tab{std::make_unique<ConfigureInput>(system_, this)}, network_tab{std::make_unique<ConfigureNetwork>(system_, this)}, profile_tab{std::make_unique<ConfigureProfileManager>(system_, this)}, - system_tab{std::make_unique<ConfigureSystem>(system_, this)}, + system_tab{std::make_unique<ConfigureSystem>(system_, nullptr, *builder, this)}, ui_tab{std::make_unique<ConfigureUi>(system_, this)}, web_tab{std::make_unique<ConfigureWeb>( this)} { Settings::SetConfiguringGlobal(true); @@ -95,6 +97,9 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, adjustSize(); ui->selectorList->setCurrentRow(0); + + // Selects the leftmost button on the bottom bar (Cancel as of writing) + ui->buttonBox->setFocus(); } ConfigureDialog::~ConfigureDialog() = default; diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 2a08b7feec..96e9a8c3e5 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -6,6 +6,9 @@ #include <memory> #include <vector> #include <QDialog> +#include "configuration/shared_widget.h" +#include "yuzu/configuration/configuration_shared.h" +#include "yuzu/configuration/shared_translation.h" #include "yuzu/vk_device_info.h" namespace Core { @@ -69,6 +72,8 @@ private: HotkeyRegistry& registry; Core::System& system; + std::unique_ptr<ConfigurationShared::Builder> builder; + std::vector<ConfigurationShared::Tab*> tab_group; std::unique_ptr<ConfigureAudio> audio_tab; std::unique_ptr<ConfigureCpu> cpu_tab; diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 2f55159f5d..c727fadd1e 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -3,57 +3,60 @@ #include <functional> #include <utility> +#include <vector> #include <QMessageBox> #include "common/settings.h" #include "core/core.h" #include "ui_configure_general.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_general.h" +#include "yuzu/configuration/shared_widget.h" #include "yuzu/uisettings.h" -ConfigureGeneral::ConfigureGeneral(const Core::System& system_, QWidget* parent) - : QWidget(parent), ui{std::make_unique<Ui::ConfigureGeneral>()}, system{system_} { +ConfigureGeneral::ConfigureGeneral(const Core::System& system_, + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_, + const ConfigurationShared::Builder& builder, QWidget* parent) + : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGeneral>()}, system{system_} { ui->setupUi(this); - SetupPerGameUI(); + Setup(builder); SetConfiguration(); - if (Settings::IsConfiguringGlobal()) { - connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, - [this]() { ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked()); }); - } - connect(ui->button_reset_defaults, &QPushButton::clicked, this, &ConfigureGeneral::ResetDefaults); + + if (!Settings::IsConfiguringGlobal()) { + ui->button_reset_defaults->setVisible(false); + } } ConfigureGeneral::~ConfigureGeneral() = default; -void ConfigureGeneral::SetConfiguration() { - const bool runtime_lock = !system.IsPoweredOn(); +void ConfigureGeneral::SetConfiguration() {} + +void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { + QLayout& layout = *ui->general_widget->layout(); - ui->use_multi_core->setEnabled(runtime_lock); - ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue()); + std::map<u32, QWidget*> hold{}; - ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); - ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.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_controller_applet_disabled->setEnabled(runtime_lock); - ui->toggle_controller_applet_disabled->setChecked( - UISettings::values.controller_applet_disabled.GetValue()); + for (const auto setting : + UISettings::values.linkage.by_category[Settings::Category::UiGeneral]) { + auto* widget = builder.BuildWidget(setting, apply_funcs); - ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue()); - ui->speed_limit->setValue(Settings::values.speed_limit.GetValue()); + if (widget == nullptr) { + continue; + } + if (!widget->Valid()) { + widget->deleteLater(); + continue; + } - ui->button_reset_defaults->setEnabled(runtime_lock); + hold.emplace(setting->Id(), widget); + } - if (Settings::IsConfiguringGlobal()) { - ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue()); - } else { - ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() && - use_speed_limit != ConfigurationShared::CheckState::Global); + for (const auto& [id, widget] : hold) { + layout.addWidget(widget); } } @@ -77,32 +80,9 @@ void ConfigureGeneral::ResetDefaults() { } void ConfigureGeneral::ApplyConfiguration() { - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core, - use_multi_core); - - if (Settings::IsConfiguringGlobal()) { - UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); - UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); - UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); - UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); - UISettings::values.controller_applet_disabled = - ui->toggle_controller_applet_disabled->isChecked(); - - // Guard if during game and set to game-specific value - if (Settings::values.use_speed_limit.UsingGlobal()) { - Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() == - Qt::Checked); - Settings::values.speed_limit.SetValue(ui->speed_limit->value()); - } - } else { - bool global_speed_limit = use_speed_limit == ConfigurationShared::CheckState::Global; - Settings::values.use_speed_limit.SetGlobal(global_speed_limit); - Settings::values.speed_limit.SetGlobal(global_speed_limit); - if (!global_speed_limit) { - Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() == - Qt::Checked); - Settings::values.speed_limit.SetValue(ui->speed_limit->value()); - } + bool powered_on = system.IsPoweredOn(); + for (const auto& func : apply_funcs) { + func(powered_on); } } @@ -117,33 +97,3 @@ void ConfigureGeneral::changeEvent(QEvent* event) { void ConfigureGeneral::RetranslateUI() { ui->retranslateUi(this); } - -void ConfigureGeneral::SetupPerGameUI() { - if (Settings::IsConfiguringGlobal()) { - // Disables each setting if: - // - A game is running (thus settings in use), and - // - A non-global setting is applied. - ui->toggle_speed_limit->setEnabled(Settings::values.use_speed_limit.UsingGlobal()); - ui->speed_limit->setEnabled(Settings::values.speed_limit.UsingGlobal()); - - return; - } - - ui->toggle_check_exit->setVisible(false); - ui->toggle_user_on_boot->setVisible(false); - ui->toggle_background_pause->setVisible(false); - ui->toggle_hide_mouse->setVisible(false); - ui->toggle_controller_applet_disabled->setVisible(false); - - ui->button_reset_defaults->setVisible(false); - - ConfigurationShared::SetColoredTristate(ui->toggle_speed_limit, - Settings::values.use_speed_limit, use_speed_limit); - ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core, - use_multi_core); - - connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() { - ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() && - (use_speed_limit != ConfigurationShared::CheckState::Global)); - }); -} diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index 7ff63f4253..2d953f6793 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h @@ -5,48 +5,49 @@ #include <functional> #include <memory> +#include <vector> #include <QWidget> +#include "yuzu/configuration/configuration_shared.h" namespace Core { class System; } class ConfigureDialog; - -namespace ConfigurationShared { -enum class CheckState; -} - class HotkeyRegistry; namespace Ui { class ConfigureGeneral; } -class ConfigureGeneral : public QWidget { - Q_OBJECT +namespace ConfigurationShared { +class Builder; +} +class ConfigureGeneral : public ConfigurationShared::Tab { public: - explicit ConfigureGeneral(const Core::System& system_, QWidget* parent = nullptr); + explicit ConfigureGeneral(const Core::System& system_, + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group, + const ConfigurationShared::Builder& builder, + QWidget* parent = nullptr); ~ConfigureGeneral() override; void SetResetCallback(std::function<void()> callback); void ResetDefaults(); - void ApplyConfiguration(); - void SetConfiguration(); + void ApplyConfiguration() override; + void SetConfiguration() override; private: + void Setup(const ConfigurationShared::Builder& builder); + void changeEvent(QEvent* event) override; void RetranslateUI(); - void SetupPerGameUI(); - std::function<void()> reset_callback; std::unique_ptr<Ui::ConfigureGeneral> ui; - ConfigurationShared::CheckState use_speed_limit; - ConfigurationShared::CheckState use_multi_core; + std::vector<std::function<void(bool)>> apply_funcs{}; const Core::System& system; }; diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index fe757d011e..a10e7d3a50 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -26,77 +26,22 @@ </property> <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> <item> - <layout class="QVBoxLayout" name="GeneralVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QCheckBox" name="toggle_speed_limit"> - <property name="text"> - <string>Limit Speed Percent</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="speed_limit"> - <property name="suffix"> - <string>%</string> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>9999</number> - </property> - <property name="value"> - <number>100</number> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QCheckBox" name="use_multi_core"> - <property name="text"> - <string>Multicore CPU Emulation</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="toggle_check_exit"> - <property name="text"> - <string>Confirm exit while emulation is running</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="toggle_user_on_boot"> - <property name="text"> - <string>Prompt for user on game boot</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="toggle_background_pause"> - <property name="text"> - <string>Pause emulation when in background</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="toggle_hide_mouse"> - <property name="text"> - <string>Hide mouse on inactivity</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="toggle_controller_applet_disabled"> - <property name="text"> - <string>Disable controller applet</string> - </property> - </widget> - </item> - </layout> + <widget class="QWidget" name="general_widget" native="true"> + <layout class="QVBoxLayout" name="GeneralVerticalLayout"> + <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> + </layout> + </widget> </item> </layout> </widget> diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index a4965524ab..a94fbc89a4 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -7,6 +7,7 @@ #include <iterator> #include <string> #include <tuple> +#include <typeinfo> #include <utility> #include <vector> #include <QBoxLayout> @@ -15,23 +16,29 @@ #include <QComboBox> #include <QIcon> #include <QLabel> +#include <QLineEdit> #include <QPixmap> #include <QPushButton> #include <QSlider> #include <QStringLiteral> #include <QtCore/qobjectdefs.h> +#include <qabstractbutton.h> +#include <qboxlayout.h> #include <qcoreevent.h> #include <qglobal.h> +#include <qgridlayout.h> #include <vulkan/vulkan_core.h> #include "common/common_types.h" #include "common/dynamic_library.h" #include "common/logging/log.h" #include "common/settings.h" +#include "common/settings_enums.h" #include "core/core.h" #include "ui_configure_graphics.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_graphics.h" +#include "yuzu/configuration/shared_widget.h" #include "yuzu/qt_common.h" #include "yuzu/uisettings.h" #include "yuzu/vk_device_info.h" @@ -46,9 +53,9 @@ static constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) { return VK_PRESENT_MODE_IMMEDIATE_KHR; case Settings::VSyncMode::Mailbox: return VK_PRESENT_MODE_MAILBOX_KHR; - case Settings::VSyncMode::FIFO: + case Settings::VSyncMode::Fifo: return VK_PRESENT_MODE_FIFO_KHR; - case Settings::VSyncMode::FIFORelaxed: + case Settings::VSyncMode::FifoRelaxed: return VK_PRESENT_MODE_FIFO_RELAXED_KHR; default: return VK_PRESENT_MODE_FIFO_KHR; @@ -62,50 +69,67 @@ static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode) case VK_PRESENT_MODE_MAILBOX_KHR: return Settings::VSyncMode::Mailbox; case VK_PRESENT_MODE_FIFO_KHR: - return Settings::VSyncMode::FIFO; + return Settings::VSyncMode::Fifo; case VK_PRESENT_MODE_FIFO_RELAXED_KHR: - return Settings::VSyncMode::FIFORelaxed; + return Settings::VSyncMode::FifoRelaxed; default: - return Settings::VSyncMode::FIFO; + return Settings::VSyncMode::Fifo; } } ConfigureGraphics::ConfigureGraphics(const Core::System& system_, std::vector<VkDeviceInfo::Record>& records_, const std::function<void()>& expose_compute_option_, - QWidget* parent) - : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, records{records_}, - expose_compute_option{expose_compute_option_}, system{system_} { + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_, + const ConfigurationShared::Builder& builder, QWidget* parent) + : ConfigurationShared::Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, + records{records_}, expose_compute_option{expose_compute_option_}, system{system_}, + combobox_translations{builder.ComboboxTranslations()}, + shader_mapping{ + combobox_translations.at(Settings::EnumMetadata<Settings::ShaderBackend>::Index())} { vulkan_device = Settings::values.vulkan_device.GetValue(); RetrieveVulkanDevices(); ui->setupUi(this); + Setup(builder); + for (const auto& device : vulkan_devices) { - ui->device->addItem(device); + vulkan_device_combobox->addItem(device); } - ui->backend->addItem(QStringLiteral("GLSL")); - ui->backend->addItem(tr("GLASM (Assembly Shaders, NVIDIA Only)")); - ui->backend->addItem(tr("SPIR-V (Experimental, Mesa Only)")); - - SetupPerGameUI(); + UpdateBackgroundColorButton(QColor::fromRgb(Settings::values.bg_red.GetValue(), + Settings::values.bg_green.GetValue(), + Settings::values.bg_blue.GetValue())); + UpdateAPILayout(); + PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout - SetConfiguration(); + // VSync setting needs to be determined after populating the VSync combobox + if (Settings::IsConfiguringGlobal()) { + const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue(); + const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting); + int index{}; + for (const auto mode : vsync_mode_combobox_enum_map) { + if (mode == vsync_mode) { + break; + } + index++; + } + if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) { + vsync_mode_combobox->setCurrentIndex(index); + } + } - connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] { + connect(api_combobox, qOverload<int>(&QComboBox::activated), this, [this] { UpdateAPILayout(); PopulateVSyncModeSelection(); - if (!Settings::IsConfiguringGlobal()) { - ConfigurationShared::SetHighlight( - ui->api_widget, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX); - } }); - connect(ui->device, qOverload<int>(&QComboBox::activated), this, [this](int device) { - UpdateDeviceSelection(device); - PopulateVSyncModeSelection(); - }); - connect(ui->backend, qOverload<int>(&QComboBox::activated), this, + connect(vulkan_device_combobox, qOverload<int>(&QComboBox::activated), this, + [this](int device) { + UpdateDeviceSelection(device); + PopulateVSyncModeSelection(); + }); + connect(shader_backend_combobox, qOverload<int>(&QComboBox::activated), this, [this](int backend) { UpdateShaderBackendSelection(backend); }); connect(ui->bg_button, &QPushButton::clicked, this, [this] { @@ -116,39 +140,45 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, UpdateBackgroundColorButton(new_bg_color); }); - ui->api->setEnabled(!UISettings::values.has_broken_vulkan && ui->api->isEnabled()); + api_combobox->setEnabled(!UISettings::values.has_broken_vulkan && api_combobox->isEnabled()); ui->api_widget->setEnabled( (!UISettings::values.has_broken_vulkan || Settings::IsConfiguringGlobal()) && ui->api_widget->isEnabled()); - ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); - ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); - connect(ui->fsr_sharpening_slider, &QSlider::valueChanged, this, - &ConfigureGraphics::SetFSRIndicatorText); - ui->fsr_sharpening_combobox->setVisible(!Settings::IsConfiguringGlobal()); - ui->fsr_sharpening_label->setVisible(Settings::IsConfiguringGlobal()); + if (Settings::IsConfiguringGlobal()) { + ui->bg_widget->setEnabled(Settings::values.bg_red.UsingGlobal()); + } } void ConfigureGraphics::PopulateVSyncModeSelection() { + if (!Settings::IsConfiguringGlobal()) { + return; + } + const Settings::RendererBackend backend{GetCurrentGraphicsBackend()}; if (backend == Settings::RendererBackend::Null) { - ui->vsync_mode_combobox->setEnabled(false); + vsync_mode_combobox->setEnabled(false); return; } - ui->vsync_mode_combobox->setEnabled(true); + vsync_mode_combobox->setEnabled(true); const int current_index = //< current selected vsync mode from combobox - ui->vsync_mode_combobox->currentIndex(); + vsync_mode_combobox->currentIndex(); const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue()) : vsync_mode_combobox_enum_map[current_index]; int index{}; - const int device{ui->device->currentIndex()}; //< current selected Vulkan device + const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device + if (device == -1) { + // Invalid device + return; + } + const auto& present_modes = //< relevant vector of present modes for the selected device or API backend == Settings::RendererBackend::Vulkan ? device_present_modes[device] : default_present_modes; - ui->vsync_mode_combobox->clear(); + vsync_mode_combobox->clear(); vsync_mode_combobox_enum_map.clear(); vsync_mode_combobox_enum_map.reserve(present_modes.size()); for (const auto present_mode : present_modes) { @@ -157,10 +187,10 @@ void ConfigureGraphics::PopulateVSyncModeSelection() { continue; } - ui->vsync_mode_combobox->insertItem(index, mode_name); + vsync_mode_combobox->insertItem(index, mode_name); vsync_mode_combobox_enum_map.push_back(present_mode); if (present_mode == current_mode) { - ui->vsync_mode_combobox->setCurrentIndex(index); + vsync_mode_combobox->setCurrentIndex(index); } index++; } @@ -186,112 +216,124 @@ void ConfigureGraphics::UpdateShaderBackendSelection(int backend) { ConfigureGraphics::~ConfigureGraphics() = default; -void ConfigureGraphics::SetConfiguration() { - const bool runtime_lock = !system.IsPoweredOn(); - - ui->api_widget->setEnabled(runtime_lock); - ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); - ui->use_disk_shader_cache->setEnabled(runtime_lock); - ui->nvdec_emulation_widget->setEnabled(runtime_lock); - ui->resolution_combobox->setEnabled(runtime_lock); - ui->accelerate_astc->setEnabled(runtime_lock); - ui->vsync_mode_layout->setEnabled(runtime_lock || - Settings::values.renderer_backend.GetValue() == - Settings::RendererBackend::Vulkan); - ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); - ui->use_asynchronous_gpu_emulation->setChecked( - Settings::values.use_asynchronous_gpu_emulation.GetValue()); - ui->accelerate_astc->setChecked(Settings::values.accelerate_astc.GetValue()); +void ConfigureGraphics::SetConfiguration() {} + +void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) { + QLayout* api_layout = ui->api_widget->layout(); + QWidget* api_grid_widget = new QWidget(this); + QVBoxLayout* api_grid_layout = new QVBoxLayout(api_grid_widget); + api_grid_layout->setContentsMargins(0, 0, 0, 0); + api_layout->addWidget(api_grid_widget); + + QLayout& graphics_layout = *ui->graphics_widget->layout(); + + std::map<u32, QWidget*> hold_graphics; + std::vector<QWidget*> hold_api; + + for (const auto setting : Settings::values.linkage.by_category[Settings::Category::Renderer]) { + ConfigurationShared::Widget* widget = [&]() { + if (setting->Id() == Settings::values.fsr_sharpening_slider.Id()) { + // FSR needs a reversed slider and a 0.5 multiplier + return builder.BuildWidget( + setting, apply_funcs, ConfigurationShared::RequestType::ReverseSlider, true, + 0.5f, nullptr, tr("%", "FSR sharpening percentage (e.g. 50%)")); + } else { + return builder.BuildWidget(setting, apply_funcs); + } + }(); - if (Settings::IsConfiguringGlobal()) { - ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); - ui->fullscreen_mode_combobox->setCurrentIndex( - static_cast<int>(Settings::values.fullscreen_mode.GetValue())); - ui->nvdec_emulation->setCurrentIndex( - static_cast<int>(Settings::values.nvdec_emulation.GetValue())); - ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); - ui->resolution_combobox->setCurrentIndex( - static_cast<int>(Settings::values.resolution_setup.GetValue())); - ui->scaling_filter_combobox->setCurrentIndex( - static_cast<int>(Settings::values.scaling_filter.GetValue())); - ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue()); - ui->anti_aliasing_combobox->setCurrentIndex( - static_cast<int>(Settings::values.anti_aliasing.GetValue())); - } else { - ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); - ConfigurationShared::SetHighlight(ui->api_widget, - !Settings::values.renderer_backend.UsingGlobal()); - - ConfigurationShared::SetPerGameSetting(ui->nvdec_emulation, - &Settings::values.nvdec_emulation); - ConfigurationShared::SetHighlight(ui->nvdec_emulation_widget, - !Settings::values.nvdec_emulation.UsingGlobal()); - - ConfigurationShared::SetPerGameSetting(ui->fullscreen_mode_combobox, - &Settings::values.fullscreen_mode); - ConfigurationShared::SetHighlight(ui->fullscreen_mode_label, - !Settings::values.fullscreen_mode.UsingGlobal()); - - ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox, - &Settings::values.aspect_ratio); - ConfigurationShared::SetHighlight(ui->ar_label, - !Settings::values.aspect_ratio.UsingGlobal()); - - ConfigurationShared::SetPerGameSetting(ui->resolution_combobox, - &Settings::values.resolution_setup); - ConfigurationShared::SetHighlight(ui->resolution_label, - !Settings::values.resolution_setup.UsingGlobal()); - - ConfigurationShared::SetPerGameSetting(ui->scaling_filter_combobox, - &Settings::values.scaling_filter); - ConfigurationShared::SetHighlight(ui->scaling_filter_label, - !Settings::values.scaling_filter.UsingGlobal()); - - ConfigurationShared::SetPerGameSetting(ui->anti_aliasing_combobox, - &Settings::values.anti_aliasing); - ConfigurationShared::SetHighlight(ui->anti_aliasing_label, - !Settings::values.anti_aliasing.UsingGlobal()); - - ui->fsr_sharpening_combobox->setCurrentIndex( - Settings::values.fsr_sharpening_slider.UsingGlobal() ? 0 : 1); - ui->fsr_sharpening_slider->setEnabled( - !Settings::values.fsr_sharpening_slider.UsingGlobal()); - ui->fsr_sharpening_value->setEnabled(!Settings::values.fsr_sharpening_slider.UsingGlobal()); - ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout, - !Settings::values.fsr_sharpening_slider.UsingGlobal()); - ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue()); - - ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1); - ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal()); - ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal()); - } - UpdateBackgroundColorButton(QColor::fromRgb(Settings::values.bg_red.GetValue(), - Settings::values.bg_green.GetValue(), - Settings::values.bg_blue.GetValue())); - UpdateAPILayout(); - PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout - SetFSRIndicatorText(ui->fsr_sharpening_slider->sliderPosition()); + if (widget == nullptr) { + continue; + } + if (!widget->Valid()) { + widget->deleteLater(); + continue; + } - // VSync setting needs to be determined after populating the VSync combobox - if (Settings::IsConfiguringGlobal()) { - const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue(); - const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting); - int index{}; - for (const auto mode : vsync_mode_combobox_enum_map) { - if (mode == vsync_mode) { - break; + if (setting->Id() == Settings::values.renderer_backend.Id()) { + // Add the renderer combobox now so it's at the top + api_grid_layout->addWidget(widget); + api_combobox = widget->combobox; + api_restore_global_button = widget->restore_button; + + if (!Settings::IsConfiguringGlobal()) { + QObject::connect(api_restore_global_button, &QAbstractButton::clicked, + [this](bool) { UpdateAPILayout(); }); + + // Detach API's restore button and place it where we want + // Lets us put it on the side, and it will automatically scale if there's a + // second combobox (shader_backend, vulkan_device) + widget->layout()->removeWidget(api_restore_global_button); + api_layout->addWidget(api_restore_global_button); } - index++; - } - if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) { - ui->vsync_mode_combobox->setCurrentIndex(index); + } else if (setting->Id() == Settings::values.vulkan_device.Id()) { + // Keep track of vulkan_device's combobox so we can populate it + hold_api.push_back(widget); + vulkan_device_combobox = widget->combobox; + vulkan_device_widget = widget; + } else if (setting->Id() == Settings::values.shader_backend.Id()) { + // Keep track of shader_backend's combobox so we can populate it + hold_api.push_back(widget); + shader_backend_combobox = widget->combobox; + shader_backend_widget = widget; + } else if (setting->Id() == Settings::values.vsync_mode.Id()) { + // Keep track of vsync_mode's combobox so we can populate it + vsync_mode_combobox = widget->combobox; + hold_graphics.emplace(setting->Id(), widget); + } else { + hold_graphics.emplace(setting->Id(), widget); } } -} -void ConfigureGraphics::SetFSRIndicatorText(int percentage) { - ui->fsr_sharpening_value->setText( - tr("%1%", "FSR sharpening percentage (e.g. 50%)").arg(100 - (percentage / 2))); + for (const auto& [id, widget] : hold_graphics) { + graphics_layout.addWidget(widget); + } + + for (auto widget : hold_api) { + api_grid_layout->addWidget(widget); + } + + // Background color is too specific to build into the new system, so we manage it here + // (3 settings, all collected into a single widget with a QColor to manage on top) + if (Settings::IsConfiguringGlobal()) { + apply_funcs.push_back([this](bool powered_on) { + Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red())); + Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green())); + Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue())); + }); + } else { + QPushButton* bg_restore_button = ConfigurationShared::Widget::CreateRestoreGlobalButton( + Settings::values.bg_red.UsingGlobal(), ui->bg_widget); + ui->bg_widget->layout()->addWidget(bg_restore_button); + + QObject::connect(bg_restore_button, &QAbstractButton::clicked, + [bg_restore_button, this](bool) { + const int r = Settings::values.bg_red.GetValue(true); + const int g = Settings::values.bg_green.GetValue(true); + const int b = Settings::values.bg_blue.GetValue(true); + UpdateBackgroundColorButton(QColor::fromRgb(r, g, b)); + + bg_restore_button->setVisible(false); + bg_restore_button->setEnabled(false); + }); + + QObject::connect(ui->bg_button, &QAbstractButton::clicked, [bg_restore_button](bool) { + bg_restore_button->setVisible(true); + bg_restore_button->setEnabled(true); + }); + + apply_funcs.push_back([bg_restore_button, this](bool powered_on) { + const bool using_global = !bg_restore_button->isEnabled(); + Settings::values.bg_red.SetGlobal(using_global); + Settings::values.bg_green.SetGlobal(using_global); + Settings::values.bg_blue.SetGlobal(using_global); + if (!using_global) { + Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red())); + Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green())); + Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue())); + } + }); + } } const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode, @@ -315,130 +357,48 @@ const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode, } } +int ConfigureGraphics::FindIndex(u32 enumeration, int value) const { + for (u32 i = 0; i < combobox_translations.at(enumeration).size(); i++) { + if (combobox_translations.at(enumeration)[i].first == static_cast<u32>(value)) { + return i; + } + } + return -1; +} + void ConfigureGraphics::ApplyConfiguration() { - const auto resolution_setup = static_cast<Settings::ResolutionSetup>( - ui->resolution_combobox->currentIndex() - - ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); - - const auto scaling_filter = static_cast<Settings::ScalingFilter>( - ui->scaling_filter_combobox->currentIndex() - - ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); - - const auto anti_aliasing = static_cast<Settings::AntiAliasing>( - ui->anti_aliasing_combobox->currentIndex() - - ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); - - ConfigurationShared::ApplyPerGameSetting(&Settings::values.fullscreen_mode, - ui->fullscreen_mode_combobox); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio, - ui->aspect_ratio_combobox); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache, - ui->use_disk_shader_cache, use_disk_shader_cache); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation, - ui->use_asynchronous_gpu_emulation, - use_asynchronous_gpu_emulation); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.accelerate_astc, ui->accelerate_astc, - accelerate_astc); + const bool powered_on = system.IsPoweredOn(); + for (const auto& func : apply_funcs) { + func(powered_on); + } if (Settings::IsConfiguringGlobal()) { - // Guard if during game and set to game-specific value - if (Settings::values.renderer_backend.UsingGlobal()) { - Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); - } - if (Settings::values.nvdec_emulation.UsingGlobal()) { - Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation()); - } - if (Settings::values.shader_backend.UsingGlobal()) { - Settings::values.shader_backend.SetValue(shader_backend); - } - if (Settings::values.vulkan_device.UsingGlobal()) { - Settings::values.vulkan_device.SetValue(vulkan_device); - } - if (Settings::values.bg_red.UsingGlobal()) { - Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red())); - Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green())); - Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue())); - } - if (Settings::values.resolution_setup.UsingGlobal()) { - Settings::values.resolution_setup.SetValue(resolution_setup); - } - if (Settings::values.scaling_filter.UsingGlobal()) { - Settings::values.scaling_filter.SetValue(scaling_filter); - } - if (Settings::values.anti_aliasing.UsingGlobal()) { - Settings::values.anti_aliasing.SetValue(anti_aliasing); - } - Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value()); - - const auto mode = vsync_mode_combobox_enum_map[ui->vsync_mode_combobox->currentIndex()]; + const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()]; const auto vsync_mode = PresentModeToSetting(mode); Settings::values.vsync_mode.SetValue(vsync_mode); - } else { - if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - Settings::values.resolution_setup.SetGlobal(true); - } else { - Settings::values.resolution_setup.SetGlobal(false); - Settings::values.resolution_setup.SetValue(resolution_setup); - } - if (ui->scaling_filter_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - Settings::values.scaling_filter.SetGlobal(true); - } else { - Settings::values.scaling_filter.SetGlobal(false); - Settings::values.scaling_filter.SetValue(scaling_filter); - } - if (ui->anti_aliasing_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - Settings::values.anti_aliasing.SetGlobal(true); - } else { - Settings::values.anti_aliasing.SetGlobal(false); - Settings::values.anti_aliasing.SetValue(anti_aliasing); - } - if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - Settings::values.renderer_backend.SetGlobal(true); - Settings::values.shader_backend.SetGlobal(true); - Settings::values.vulkan_device.SetGlobal(true); - } else { - Settings::values.renderer_backend.SetGlobal(false); - Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); - switch (GetCurrentGraphicsBackend()) { - case Settings::RendererBackend::OpenGL: - case Settings::RendererBackend::Null: - Settings::values.shader_backend.SetGlobal(false); - Settings::values.vulkan_device.SetGlobal(true); - Settings::values.shader_backend.SetValue(shader_backend); - break; - case Settings::RendererBackend::Vulkan: - Settings::values.shader_backend.SetGlobal(true); - Settings::values.vulkan_device.SetGlobal(false); - Settings::values.vulkan_device.SetValue(vulkan_device); - break; - } - } - - if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - Settings::values.nvdec_emulation.SetGlobal(true); - } else { - Settings::values.nvdec_emulation.SetGlobal(false); - Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation()); - } - - if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - Settings::values.bg_red.SetGlobal(true); - Settings::values.bg_green.SetGlobal(true); - Settings::values.bg_blue.SetGlobal(true); - } else { - Settings::values.bg_red.SetGlobal(false); - Settings::values.bg_green.SetGlobal(false); - Settings::values.bg_blue.SetGlobal(false); - Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red())); - Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green())); - Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue())); - } + } - if (ui->fsr_sharpening_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - Settings::values.fsr_sharpening_slider.SetGlobal(true); - } else { - Settings::values.fsr_sharpening_slider.SetGlobal(false); - Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value()); + Settings::values.vulkan_device.SetGlobal(true); + Settings::values.shader_backend.SetGlobal(true); + if (Settings::IsConfiguringGlobal() || + (!Settings::IsConfiguringGlobal() && api_restore_global_button->isEnabled())) { + auto backend = static_cast<Settings::RendererBackend>( + combobox_translations + .at(Settings::EnumMetadata< + Settings::RendererBackend>::Index())[api_combobox->currentIndex()] + .first); + switch (backend) { + case Settings::RendererBackend::OpenGL: + Settings::values.shader_backend.SetGlobal(Settings::IsConfiguringGlobal()); + Settings::values.shader_backend.SetValue(static_cast<Settings::ShaderBackend>( + shader_mapping[shader_backend_combobox->currentIndex()].first)); + break; + case Settings::RendererBackend::Vulkan: + Settings::values.vulkan_device.SetGlobal(Settings::IsConfiguringGlobal()); + Settings::values.vulkan_device.SetValue(vulkan_device_combobox->currentIndex()); + break; + case Settings::RendererBackend::Null: + break; } } } @@ -466,36 +426,26 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) { } void ConfigureGraphics::UpdateAPILayout() { - if (!Settings::IsConfiguringGlobal() && - ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - vulkan_device = Settings::values.vulkan_device.GetValue(true); - shader_backend = Settings::values.shader_backend.GetValue(true); - ui->device_widget->setEnabled(false); - ui->backend_widget->setEnabled(false); - } else { - vulkan_device = Settings::values.vulkan_device.GetValue(); - shader_backend = Settings::values.shader_backend.GetValue(); - ui->device_widget->setEnabled(true); - ui->backend_widget->setEnabled(true); - } - - switch (GetCurrentGraphicsBackend()) { - case Settings::RendererBackend::OpenGL: - ui->backend->setCurrentIndex(static_cast<u32>(shader_backend)); - ui->device_widget->setVisible(false); - ui->backend_widget->setVisible(true); - break; - case Settings::RendererBackend::Vulkan: - if (static_cast<int>(vulkan_device) < ui->device->count()) { - ui->device->setCurrentIndex(vulkan_device); - } - ui->device_widget->setVisible(true); - ui->backend_widget->setVisible(false); - break; - case Settings::RendererBackend::Null: - ui->device_widget->setVisible(false); - ui->backend_widget->setVisible(false); - break; + bool runtime_lock = !system.IsPoweredOn(); + bool need_global = !(Settings::IsConfiguringGlobal() || api_restore_global_button->isEnabled()); + vulkan_device = Settings::values.vulkan_device.GetValue(need_global); + shader_backend = Settings::values.shader_backend.GetValue(need_global); + vulkan_device_widget->setEnabled(!need_global && runtime_lock); + shader_backend_widget->setEnabled(!need_global && runtime_lock); + + const auto current_backend = GetCurrentGraphicsBackend(); + const bool is_opengl = current_backend == Settings::RendererBackend::OpenGL; + const bool is_vulkan = current_backend == Settings::RendererBackend::Vulkan; + + vulkan_device_widget->setVisible(is_vulkan); + shader_backend_widget->setVisible(is_opengl); + + if (is_opengl) { + shader_backend_combobox->setCurrentIndex( + FindIndex(Settings::EnumMetadata<Settings::ShaderBackend>::Index(), + static_cast<int>(shader_backend))); + } else if (is_vulkan && static_cast<int>(vulkan_device) < vulkan_device_combobox->count()) { + vulkan_device_combobox->setCurrentIndex(vulkan_device); } } @@ -515,92 +465,11 @@ void ConfigureGraphics::RetrieveVulkanDevices() { } Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { - if (Settings::IsConfiguringGlobal()) { - return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); - } - - if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - Settings::values.renderer_backend.SetGlobal(true); - return Settings::values.renderer_backend.GetValue(); + if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) { + return Settings::values.renderer_backend.GetValue(true); } - Settings::values.renderer_backend.SetGlobal(false); - return static_cast<Settings::RendererBackend>(ui->api->currentIndex() - - ConfigurationShared::USE_GLOBAL_OFFSET); -} - -Settings::NvdecEmulation ConfigureGraphics::GetCurrentNvdecEmulation() const { - if (Settings::IsConfiguringGlobal()) { - return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex()); - } - - if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - Settings::values.nvdec_emulation.SetGlobal(true); - return Settings::values.nvdec_emulation.GetValue(); - } - Settings::values.nvdec_emulation.SetGlobal(false); - return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex() - - ConfigurationShared::USE_GLOBAL_OFFSET); -} - -void ConfigureGraphics::SetupPerGameUI() { - if (Settings::IsConfiguringGlobal()) { - ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal()); - ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal()); - ui->fullscreen_mode_combobox->setEnabled(Settings::values.fullscreen_mode.UsingGlobal()); - ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); - ui->resolution_combobox->setEnabled(Settings::values.resolution_setup.UsingGlobal()); - ui->scaling_filter_combobox->setEnabled(Settings::values.scaling_filter.UsingGlobal()); - ui->fsr_sharpening_slider->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal()); - ui->anti_aliasing_combobox->setEnabled(Settings::values.anti_aliasing.UsingGlobal()); - ui->use_asynchronous_gpu_emulation->setEnabled( - Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); - ui->nvdec_emulation->setEnabled(Settings::values.nvdec_emulation.UsingGlobal()); - ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal()); - ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal()); - ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal()); - ui->fsr_slider_layout->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal()); - - return; - } - - connect(ui->bg_combobox, qOverload<int>(&QComboBox::activated), this, [this](int index) { - ui->bg_button->setEnabled(index == 1); - ConfigurationShared::SetHighlight(ui->bg_layout, index == 1); - }); - - connect(ui->fsr_sharpening_combobox, qOverload<int>(&QComboBox::activated), this, - [this](int index) { - ui->fsr_sharpening_slider->setEnabled(index == 1); - ui->fsr_sharpening_value->setEnabled(index == 1); - ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout, index == 1); - }); - - ConfigurationShared::SetColoredTristate( - ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache); - ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc, - accelerate_astc); - ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation, - Settings::values.use_asynchronous_gpu_emulation, - use_asynchronous_gpu_emulation); - - ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label, - Settings::values.aspect_ratio.GetValue(true)); - ConfigurationShared::SetColoredComboBox( - ui->fullscreen_mode_combobox, ui->fullscreen_mode_label, - static_cast<int>(Settings::values.fullscreen_mode.GetValue(true))); - ConfigurationShared::SetColoredComboBox( - ui->resolution_combobox, ui->resolution_label, - static_cast<int>(Settings::values.resolution_setup.GetValue(true))); - ConfigurationShared::SetColoredComboBox( - ui->scaling_filter_combobox, ui->scaling_filter_label, - static_cast<int>(Settings::values.scaling_filter.GetValue(true))); - ConfigurationShared::SetColoredComboBox( - ui->anti_aliasing_combobox, ui->anti_aliasing_label, - static_cast<int>(Settings::values.anti_aliasing.GetValue(true))); - ConfigurationShared::InsertGlobalItem( - ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); - ConfigurationShared::InsertGlobalItem( - ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); - - ui->vsync_mode_layout->setVisible(false); + return static_cast<Settings::RendererBackend>( + combobox_translations.at(Settings::EnumMetadata<Settings::RendererBackend>::Index()) + .at(api_combobox->currentIndex()) + .first); } diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index be9310b748..02d9b00f17 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -5,6 +5,8 @@ #include <functional> #include <memory> +#include <type_traits> +#include <typeindex> #include <vector> #include <QColor> #include <QString> @@ -12,10 +14,14 @@ #include <qobjectdefs.h> #include <vulkan/vulkan_core.h> #include "common/common_types.h" +#include "configuration/shared_translation.h" #include "vk_device_info.h" +#include "yuzu/configuration/configuration_shared.h" +class QPushButton; class QEvent; class QObject; +class QComboBox; namespace Settings { enum class NvdecEmulation : u32; @@ -27,31 +33,33 @@ namespace Core { class System; } -namespace ConfigurationShared { -enum class CheckState; -} - namespace Ui { class ConfigureGraphics; } -class ConfigureGraphics : public QWidget { - Q_OBJECT +namespace ConfigurationShared { +class Builder; +} +class ConfigureGraphics : public ConfigurationShared::Tab { public: explicit ConfigureGraphics(const Core::System& system_, std::vector<VkDeviceInfo::Record>& records, const std::function<void()>& expose_compute_option_, + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group, + const ConfigurationShared::Builder& builder, QWidget* parent = nullptr); ~ConfigureGraphics() override; - void ApplyConfiguration(); - void SetConfiguration(); + void ApplyConfiguration() override; + void SetConfiguration() override; private: void changeEvent(QEvent* event) override; void RetranslateUI(); + void Setup(const ConfigurationShared::Builder& builder); + void PopulateVSyncModeSelection(); void UpdateBackgroundColorButton(QColor color); void UpdateAPILayout(); @@ -60,34 +68,40 @@ private: void RetrieveVulkanDevices(); - void SetFSRIndicatorText(int percentage); /* Turns a Vulkan present mode into a textual string for a UI * (and eventually for a human to read) */ const QString TranslateVSyncMode(VkPresentModeKHR mode, Settings::RendererBackend backend) const; - void SetupPerGameUI(); - Settings::RendererBackend GetCurrentGraphicsBackend() const; - Settings::NvdecEmulation GetCurrentNvdecEmulation() const; + + int FindIndex(u32 enumeration, int value) const; std::unique_ptr<Ui::ConfigureGraphics> ui; QColor bg_color; - ConfigurationShared::CheckState use_nvdec_emulation; - ConfigurationShared::CheckState accelerate_astc; - ConfigurationShared::CheckState use_disk_shader_cache; - ConfigurationShared::CheckState use_asynchronous_gpu_emulation; + std::vector<std::function<void(bool)>> apply_funcs{}; std::vector<VkDeviceInfo::Record>& records; std::vector<QString> vulkan_devices; std::vector<std::vector<VkPresentModeKHR>> device_present_modes; std::vector<VkPresentModeKHR> - vsync_mode_combobox_enum_map; //< Keeps track of which present mode corresponds to which - // selection in the combobox + vsync_mode_combobox_enum_map{}; //< Keeps track of which present mode corresponds to which + // selection in the combobox u32 vulkan_device{}; Settings::ShaderBackend shader_backend{}; const std::function<void()>& expose_compute_option; const Core::System& system; + const ConfigurationShared::ComboboxTranslationMap& combobox_translations; + const std::vector<std::pair<u32, QString>>& shader_mapping; + + QPushButton* api_restore_global_button; + QComboBox* vulkan_device_combobox; + QComboBox* api_combobox; + QComboBox* shader_backend_combobox; + QComboBox* vsync_mode_combobox; + QWidget* vulkan_device_widget; + QWidget* api_widget; + QWidget* shader_backend_widget; }; diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 39f70e406f..d09415d702 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -27,7 +27,7 @@ <layout class="QVBoxLayout" name="verticalLayout_3"> <item> <widget class="QWidget" name="api_widget" native="true"> - <layout class="QGridLayout" name="gridLayout"> + <layout class="QHBoxLayout" name="horizontalLayout"> <property name="leftMargin"> <number>0</number> </property> @@ -40,115 +40,6 @@ <property name="bottomMargin"> <number>0</number> </property> - <property name="horizontalSpacing"> - <number>6</number> - </property> - <item row="4" column="0"> - <widget class="QWidget" name="backend_widget" native="true"> - <layout class="QHBoxLayout" name="backend_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="QLabel" name="backend_label"> - <property name="text"> - <string>Shader Backend:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="backend"/> - </item> - </layout> - </widget> - </item> - <item row="2" column="0"> - <widget class="QWidget" name="device_widget" native="true"> - <layout class="QHBoxLayout" name="device_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="QLabel" name="device_label"> - <property name="text"> - <string>Device:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="device"/> - </item> - </layout> - </widget> - </item> - <item row="0" column="0"> - <widget class="QWidget" name="api_layout_2" native="true"> - <layout class="QHBoxLayout" name="api_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="QLabel" name="api_label"> - <property name="text"> - <string>API:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="api"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <item> - <property name="text"> - <string notr="true">OpenGL</string> - </property> - </item> - <item> - <property name="text"> - <string notr="true">Vulkan</string> - </property> - </item> - <item> - <property name="text"> - <string>None</string> - </property> - </item> - </widget> - </item> - </layout> - </widget> - </item> </layout> </widget> </item> @@ -168,111 +59,8 @@ </property> <layout class="QVBoxLayout" name="verticalLayout_4"> <item> - <widget class="QCheckBox" name="use_disk_shader_cache"> - <property name="text"> - <string>Use disk pipeline cache</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> - <property name="text"> - <string>Use asynchronous GPU emulation</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="accelerate_astc"> - <property name="text"> - <string>Accelerate ASTC texture decoding</string> - </property> - </widget> - </item> - <item> - <widget class="QWidget" name="vsync_mode_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <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="vsync_mode_label"> - <property name="text"> - <string>VSync Mode:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="vsync_mode_combobox"> - <property name="toolTip"> - <string>FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. -FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down. -Mailbox can have lower latency than FIFO and does not tear but may drop frames. -Immediate (no synchronization) just presents whatever is available and can exhibit tearing.</string> - </property> - <property name="currentText"> - <string/> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="nvdec_emulation_widget" native="true"> - <layout class="QHBoxLayout" name="nvdec_emulation_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="QLabel" name="nvdec_emulation_label"> - <property name="text"> - <string>NVDEC emulation:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="nvdec_emulation"> - <item> - <property name="text"> - <string>No Video Output</string> - </property> - </item> - <item> - <property name="text"> - <string>CPU Video Decoding</string> - </property> - </item> - <item> - <property name="text"> - <string>GPU Video Decoding (Default)</string> - </property> - </item> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="fullscreen_mode_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_1"> + <widget class="QWidget" name="graphics_widget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout"> <property name="leftMargin"> <number>0</number> </property> @@ -285,33 +73,12 @@ Immediate (no synchronization) just presents whatever is available and can exhib <property name="bottomMargin"> <number>0</number> </property> - <item> - <widget class="QLabel" name="fullscreen_mode_label"> - <property name="text"> - <string>Fullscreen Mode:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="fullscreen_mode_combobox"> - <item> - <property name="text"> - <string>Borderless Windowed</string> - </property> - </item> - <item> - <property name="text"> - <string>Exclusive Fullscreen</string> - </property> - </item> - </widget> - </item> </layout> </widget> </item> <item> - <widget class="QWidget" name="aspect_ratio_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_2"> + <widget class="QWidget" name="bg_widget" native="true"> + <layout class="QHBoxLayout" name="bg_layout"> <property name="leftMargin"> <number>0</number> </property> @@ -325,452 +92,35 @@ Immediate (no synchronization) just presents whatever is available and can exhib <number>0</number> </property> <item> - <widget class="QLabel" name="ar_label"> - <property name="text"> - <string>Aspect Ratio:</string> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="aspect_ratio_combobox"> - <item> - <property name="text"> - <string>Default (16:9)</string> - </property> - </item> - <item> - <property name="text"> - <string>Force 4:3</string> - </property> - </item> - <item> - <property name="text"> - <string>Force 21:9</string> - </property> - </item> - <item> - <property name="text"> - <string>Force 16:10</string> - </property> - </item> - <item> - <property name="text"> - <string>Stretch to Window</string> - </property> - </item> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="resolution_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_5"> - <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="resolution_label"> - <property name="text"> - <string>Resolution:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="resolution_combobox"> - <item> - <property name="text"> - <string>0.5X (360p/540p) [EXPERIMENTAL]</string> - </property> - </item> - <item> - <property name="text"> - <string>0.75X (540p/810p) [EXPERIMENTAL]</string> - </property> - </item> - <item> - <property name="text"> - <string>1X (720p/1080p)</string> - </property> - </item> - <item> - <property name="text"> - <string>1.5X (1080p/1620p) [EXPERIMENTAL]</string> - </property> - </item> - <item> - <property name="text"> - <string>2X (1440p/2160p)</string> - </property> - </item> - <item> - <property name="text"> - <string>3X (2160p/3240p)</string> - </property> - </item> - <item> - <property name="text"> - <string>4X (2880p/4320p)</string> - </property> - </item> - <item> - <property name="text"> - <string>5X (3600p/5400p)</string> - </property> - </item> - <item> - <property name="text"> - <string>6X (4320p/6480p)</string> - </property> - </item> - <item> - <property name="text"> - <string>7X (5040p/7560p)</string> - </property> - </item> - <item> - <property name="text"> - <string>8X (5760p/8640p)</string> - </property> - </item> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="scaling_filter_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_6"> - <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="scaling_filter_label"> - <property name="text"> - <string>Window Adapting Filter:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="scaling_filter_combobox"> - <item> - <property name="text"> - <string>Nearest Neighbor</string> - </property> - </item> - <item> - <property name="text"> - <string>Bilinear</string> - </property> - </item> - <item> - <property name="text"> - <string>Bicubic</string> - </property> - </item> - <item> - <property name="text"> - <string>Gaussian</string> - </property> - </item> - <item> - <property name="text"> - <string>ScaleForce</string> - </property> - </item> - <item> - <property name="text"> - <string>AMD FidelityFX™️ Super Resolution</string> - </property> - </item> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="anti_aliasing_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_7"> - <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="anti_aliasing_label"> - <property name="text"> - <string>Anti-Aliasing Method:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="anti_aliasing_combobox"> - <item> - <property name="text"> - <string>None</string> - </property> - </item> - <item> - <property name="text"> - <string>FXAA</string> - </property> - </item> - <item> - <property name="text"> - <string>SMAA</string> - </property> - </item> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="fsr_sharpening_layout" native="true"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="spacing"> - <number>6</number> - </property> - <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> - </property> - <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> - <layout class="QHBoxLayout" name="fsr_sharpening_label_group"> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QComboBox" name="fsr_sharpening_combobox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <item> - <property name="text"> - <string>Use global FSR Sharpness</string> - </property> - </item> - <item> - <property name="text"> - <string>Set FSR Sharpness</string> - </property> - </item> - </widget> - </item> - <item> - <widget class="QLabel" name="fsr_sharpening_label"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>FSR Sharpness:</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="fsr_slider_layout"> - <property name="spacing"> - <number>6</number> - </property> - <item> - <widget class="QSlider" name="fsr_sharpening_slider"> - <property name="sizePolicy"> - <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="baseSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="maximum"> - <number>200</number> - </property> - <property name="sliderPosition"> - <number>25</number> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="invertedAppearance"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="fsr_sharpening_value"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>32</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>100%</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="bg_layout" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <property name="spacing"> - <number>6</number> - </property> - <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="bg_combobox"> - <property name="currentText"> - <string>Use global background color</string> - </property> - <property name="currentIndex"> - <number>0</number> - </property> - <property name="maxVisibleItems"> - <number>10</number> - </property> - <item> - <property name="text"> - <string>Use global background color</string> - </property> - </item> - <item> - <property name="text"> - <string>Set background color:</string> - </property> - </item> - </widget> - </item> - <item> - <widget class="QLabel" name="bg_label"> <property name="text"> <string>Background Color:</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="QPushButton" name="bg_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="maximumSize"> <size> <width>40</width> <height>16777215</height> </size> </property> + <property name="text"> + <string/> + </property> </widget> </item> </layout> diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index c0a044767a..4db18673d4 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -1,104 +1,68 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <vector> +#include <QLabel> +#include <qnamespace.h> #include "common/settings.h" #include "core/core.h" #include "ui_configure_graphics_advanced.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_graphics_advanced.h" +#include "yuzu/configuration/shared_translation.h" +#include "yuzu/configuration/shared_widget.h" -ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(const Core::System& system_, QWidget* parent) - : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphicsAdvanced>()}, system{system_} { +ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced( + const Core::System& system_, std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_, + const ConfigurationShared::Builder& builder, QWidget* parent) + : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGraphicsAdvanced>()}, system{system_} { ui->setupUi(this); - SetupPerGameUI(); + Setup(builder); SetConfiguration(); - ui->enable_compute_pipelines_checkbox->setVisible(false); + checkbox_enable_compute_pipelines->setVisible(false); } ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default; -void ConfigureGraphicsAdvanced::SetConfiguration() { - const bool runtime_lock = !system.IsPoweredOn(); - ui->use_reactive_flushing->setEnabled(runtime_lock); - ui->async_present->setEnabled(runtime_lock); - ui->renderer_force_max_clock->setEnabled(runtime_lock); - ui->async_astc->setEnabled(runtime_lock); - ui->astc_recompression_combobox->setEnabled(runtime_lock); - ui->use_asynchronous_shaders->setEnabled(runtime_lock); - ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); - ui->enable_compute_pipelines_checkbox->setEnabled(runtime_lock); - - ui->async_present->setChecked(Settings::values.async_presentation.GetValue()); - ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue()); - ui->use_reactive_flushing->setChecked(Settings::values.use_reactive_flushing.GetValue()); - ui->async_astc->setChecked(Settings::values.async_astc.GetValue()); - ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); - ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); - ui->use_vulkan_driver_pipeline_cache->setChecked( - Settings::values.use_vulkan_driver_pipeline_cache.GetValue()); - ui->enable_compute_pipelines_checkbox->setChecked( - Settings::values.enable_compute_pipelines.GetValue()); - ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue()); - ui->barrier_feedback_loops_checkbox->setChecked( - Settings::values.barrier_feedback_loops.GetValue()); - - if (Settings::IsConfiguringGlobal()) { - ui->gpu_accuracy->setCurrentIndex( - static_cast<int>(Settings::values.gpu_accuracy.GetValue())); - ui->anisotropic_filtering_combobox->setCurrentIndex( - Settings::values.max_anisotropy.GetValue()); - ui->astc_recompression_combobox->setCurrentIndex( - static_cast<int>(Settings::values.astc_recompression.GetValue())); - } else { - ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy); - ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox, - &Settings::values.max_anisotropy); - ConfigurationShared::SetPerGameSetting(ui->astc_recompression_combobox, - &Settings::values.astc_recompression); - ConfigurationShared::SetHighlight(ui->label_gpu_accuracy, - !Settings::values.gpu_accuracy.UsingGlobal()); - ConfigurationShared::SetHighlight(ui->af_label, - !Settings::values.max_anisotropy.UsingGlobal()); - ConfigurationShared::SetHighlight(ui->label_astc_recompression, - !Settings::values.astc_recompression.UsingGlobal()); +void ConfigureGraphicsAdvanced::SetConfiguration() {} + +void ConfigureGraphicsAdvanced::Setup(const ConfigurationShared::Builder& builder) { + auto& layout = *ui->populate_target->layout(); + std::map<u32, QWidget*> hold{}; // A map will sort the data for us + + for (auto setting : + Settings::values.linkage.by_category[Settings::Category::RendererAdvanced]) { + ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs); + + if (widget == nullptr) { + continue; + } + if (!widget->Valid()) { + widget->deleteLater(); + continue; + } + + hold.emplace(setting->Id(), widget); + + // Keep track of enable_compute_pipelines so we can display it when needed + if (setting->Id() == Settings::values.enable_compute_pipelines.Id()) { + checkbox_enable_compute_pipelines = widget; + } + } + for (const auto& [id, widget] : hold) { + layout.addWidget(widget); } } void ConfigureGraphicsAdvanced::ApplyConfiguration() { - ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_presentation, - ui->async_present, async_present); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.renderer_force_max_clock, - ui->renderer_force_max_clock, - renderer_force_max_clock); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, - ui->anisotropic_filtering_combobox); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_reactive_flushing, - ui->use_reactive_flushing, use_reactive_flushing); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_astc, ui->async_astc, - async_astc); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.astc_recompression, - ui->astc_recompression_combobox); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, - ui->use_asynchronous_shaders, - use_asynchronous_shaders); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, - ui->use_fast_gpu_time, use_fast_gpu_time); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache, - ui->use_vulkan_driver_pipeline_cache, - use_vulkan_driver_pipeline_cache); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines, - ui->enable_compute_pipelines_checkbox, - enable_compute_pipelines); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate, - ui->use_video_framerate_checkbox, use_video_framerate); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.barrier_feedback_loops, - ui->barrier_feedback_loops_checkbox, - barrier_feedback_loops); + const bool is_powered_on = system.IsPoweredOn(); + for (const auto& func : apply_funcs) { + func(is_powered_on); + } } void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { @@ -113,71 +77,6 @@ void ConfigureGraphicsAdvanced::RetranslateUI() { ui->retranslateUi(this); } -void ConfigureGraphicsAdvanced::SetupPerGameUI() { - // Disable if not global (only happens during game) - if (Settings::IsConfiguringGlobal()) { - ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); - ui->async_present->setEnabled(Settings::values.async_presentation.UsingGlobal()); - ui->renderer_force_max_clock->setEnabled( - Settings::values.renderer_force_max_clock.UsingGlobal()); - ui->use_reactive_flushing->setEnabled(Settings::values.use_reactive_flushing.UsingGlobal()); - ui->async_astc->setEnabled(Settings::values.async_astc.UsingGlobal()); - ui->astc_recompression_combobox->setEnabled( - Settings::values.astc_recompression.UsingGlobal()); - ui->use_asynchronous_shaders->setEnabled( - Settings::values.use_asynchronous_shaders.UsingGlobal()); - ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); - ui->use_vulkan_driver_pipeline_cache->setEnabled( - Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal()); - ui->anisotropic_filtering_combobox->setEnabled( - Settings::values.max_anisotropy.UsingGlobal()); - ui->enable_compute_pipelines_checkbox->setEnabled( - Settings::values.enable_compute_pipelines.UsingGlobal()); - ui->use_video_framerate_checkbox->setEnabled( - Settings::values.use_video_framerate.UsingGlobal()); - ui->barrier_feedback_loops_checkbox->setEnabled( - Settings::values.barrier_feedback_loops.UsingGlobal()); - - return; - } - - ConfigurationShared::SetColoredTristate(ui->async_present, Settings::values.async_presentation, - async_present); - ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock, - Settings::values.renderer_force_max_clock, - renderer_force_max_clock); - ConfigurationShared::SetColoredTristate( - ui->use_reactive_flushing, Settings::values.use_reactive_flushing, use_reactive_flushing); - ConfigurationShared::SetColoredTristate(ui->async_astc, Settings::values.async_astc, - async_astc); - ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, - Settings::values.use_asynchronous_shaders, - use_asynchronous_shaders); - ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, - Settings::values.use_fast_gpu_time, use_fast_gpu_time); - ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache, - Settings::values.use_vulkan_driver_pipeline_cache, - use_vulkan_driver_pipeline_cache); - ConfigurationShared::SetColoredTristate(ui->enable_compute_pipelines_checkbox, - Settings::values.enable_compute_pipelines, - enable_compute_pipelines); - ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox, - Settings::values.use_video_framerate, - use_video_framerate); - ConfigurationShared::SetColoredTristate(ui->barrier_feedback_loops_checkbox, - Settings::values.barrier_feedback_loops, - barrier_feedback_loops); - ConfigurationShared::SetColoredComboBox( - ui->gpu_accuracy, ui->label_gpu_accuracy, - static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); - ConfigurationShared::SetColoredComboBox( - ui->anisotropic_filtering_combobox, ui->af_label, - static_cast<int>(Settings::values.max_anisotropy.GetValue(true))); - ConfigurationShared::SetColoredComboBox( - ui->astc_recompression_combobox, ui->label_astc_recompression, - static_cast<int>(Settings::values.astc_recompression.GetValue(true))); -} - void ConfigureGraphicsAdvanced::ExposeComputeOption() { - ui->enable_compute_pipelines_checkbox->setVisible(true); + checkbox_enable_compute_pipelines->setVisible(true); } diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index 369a7c83e2..78b5389c32 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h @@ -4,51 +4,44 @@ #pragma once #include <memory> +#include <vector> #include <QWidget> +#include "yuzu/configuration/configuration_shared.h" namespace Core { class System; } -namespace ConfigurationShared { -enum class CheckState; -} - namespace Ui { class ConfigureGraphicsAdvanced; } -class ConfigureGraphicsAdvanced : public QWidget { - Q_OBJECT +namespace ConfigurationShared { +class Builder; +} +class ConfigureGraphicsAdvanced : public ConfigurationShared::Tab { public: - explicit ConfigureGraphicsAdvanced(const Core::System& system_, QWidget* parent = nullptr); + explicit ConfigureGraphicsAdvanced( + const Core::System& system_, std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group, + const ConfigurationShared::Builder& builder, QWidget* parent = nullptr); ~ConfigureGraphicsAdvanced() override; - void ApplyConfiguration(); - void SetConfiguration(); + void ApplyConfiguration() override; + void SetConfiguration() override; void ExposeComputeOption(); private: + void Setup(const ConfigurationShared::Builder& builder); void changeEvent(QEvent* event) override; void RetranslateUI(); - void SetupPerGameUI(); - std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; - ConfigurationShared::CheckState async_present; - ConfigurationShared::CheckState renderer_force_max_clock; - ConfigurationShared::CheckState use_vsync; - ConfigurationShared::CheckState async_astc; - ConfigurationShared::CheckState use_reactive_flushing; - ConfigurationShared::CheckState use_asynchronous_shaders; - ConfigurationShared::CheckState use_fast_gpu_time; - ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache; - ConfigurationShared::CheckState enable_compute_pipelines; - ConfigurationShared::CheckState use_video_framerate; - ConfigurationShared::CheckState barrier_feedback_loops; - const Core::System& system; + + std::vector<std::function<void(bool)>> apply_funcs; + + QWidget* checkbox_enable_compute_pipelines{}; }; diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index d527a6f38b..37a854ca3f 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui @@ -26,8 +26,8 @@ </property> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> - <widget class="QWidget" name="gpu_accuracy_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_2"> + <widget class="QWidget" name="populate_target" native="true"> + <layout class="QVBoxLayout" name="verticalLayout"> <property name="leftMargin"> <number>0</number> </property> @@ -40,233 +40,6 @@ <property name="bottomMargin"> <number>0</number> </property> - <item> - <widget class="QLabel" name="label_gpu_accuracy"> - <property name="text"> - <string>Accuracy Level:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="gpu_accuracy"> - <item> - <property name="text"> - <string notr="true">Normal</string> - </property> - </item> - <item> - <property name="text"> - <string notr="true">High</string> - </property> - </item> - <item> - <property name="text"> - <string notr="true">Extreme(very slow)</string> - </property> - </item> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="astc_recompression_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <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_astc_recompression"> - <property name="text"> - <string>ASTC recompression:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="astc_recompression_combobox"> - <item> - <property name="text"> - <string>Uncompressed (Best quality)</string> - </property> - </item> - <item> - <property name="text"> - <string>BC1 (Low quality)</string> - </property> - </item> - <item> - <property name="text"> - <string>BC3 (Medium quality)</string> - </property> - </item> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QCheckBox" name="async_present"> - <property name="text"> - <string>Enable asynchronous presentation (Vulkan only)</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="renderer_force_max_clock"> - <property name="toolTip"> - <string>Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed.</string> - </property> - <property name="text"> - <string>Force maximum clocks (Vulkan only)</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="async_astc"> - <property name="toolTip"> - <string>Enables asynchronous ASTC texture decoding, which may reduce load time stutter. This feature is experimental.</string> - </property> - <property name="text"> - <string>Decode ASTC textures asynchronously (Hack)</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="use_reactive_flushing"> - <property name="toolTip"> - <string>Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.</string> - </property> - <property name="text"> - <string>Enable Reactive Flushing</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="use_asynchronous_shaders"> - <property name="toolTip"> - <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string> - </property> - <property name="text"> - <string>Use asynchronous shader building (Hack)</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="use_fast_gpu_time"> - <property name="toolTip"> - <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string> - </property> - <property name="text"> - <string>Use Fast GPU Time (Hack)</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="use_vulkan_driver_pipeline_cache"> - <property name="toolTip"> - <string>Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally.</string> - </property> - <property name="text"> - <string>Use Vulkan pipeline cache</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="enable_compute_pipelines_checkbox"> - <property name="toolTip"> - <string>Enable compute pipelines, required by some games. This setting only exists for Intel proprietary drivers, and may crash if enabled. -Compute pipelines are always enabled on all other drivers.</string> - </property> - <property name="text"> - <string>Enable Compute Pipelines (Intel Vulkan only)</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="use_video_framerate_checkbox"> - <property name="toolTip"> - <string>Run the game at normal speed during video playback, even when the framerate is unlocked.</string> - </property> - <property name="text"> - <string>Sync to framerate of video playback</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="barrier_feedback_loops_checkbox"> - <property name="toolTip"> - <string>Improves rendering of transparency effects in specific games.</string> - </property> - <property name="text"> - <string>Barrier feedback loops</string> - </property> - </widget> - </item> - <item> - <widget class="QWidget" name="af_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_1"> - <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="af_label"> - <property name="text"> - <string>Anisotropic Filtering:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="anisotropic_filtering_combobox"> - <item> - <property name="text"> - <string>Automatic</string> - </property> - </item> - <item> - <property name="text"> - <string>Default</string> - </property> - </item> - <item> - <property name="text"> - <string>2x</string> - </property> - </item> - <item> - <property name="text"> - <string>4x</string> - </property> - </item> - <item> - <property name="text"> - <string>8x</string> - </property> - </item> - <item> - <property name="text"> - <string>16x</string> - </property> - </item> - </widget> - </item> </layout> </widget> </item> diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index eb96e6068c..cd8b3012e8 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -17,6 +17,7 @@ #include <QTimer> #include "common/fs/fs_util.h" +#include "configuration/shared_widget.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" @@ -24,9 +25,9 @@ #include "core/loader/loader.h" #include "ui_configure_per_game.h" #include "yuzu/configuration/config.h" +#include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_audio.h" #include "yuzu/configuration/configure_cpu.h" -#include "yuzu/configuration/configure_general.h" #include "yuzu/configuration/configure_graphics.h" #include "yuzu/configuration/configure_graphics_advanced.h" #include "yuzu/configuration/configure_input_per_game.h" @@ -41,26 +42,28 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st std::vector<VkDeviceInfo::Record>& vk_device_records, Core::System& system_) : QDialog(parent), - ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_} { + ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_}, + builder{std::make_unique<ConfigurationShared::Builder>(this, !system_.IsPoweredOn())}, + tab_group{std::make_shared<std::vector<ConfigurationShared::Tab*>>()} { const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) : fmt::format("{:016X}", title_id); game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); - audio_tab = std::make_unique<ConfigureAudio>(system_, this); - cpu_tab = std::make_unique<ConfigureCpu>(system_, this); - general_tab = std::make_unique<ConfigureGeneral>(system_, this); - graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this); + audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this); + cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this); + graphics_advanced_tab = + std::make_unique<ConfigureGraphicsAdvanced>(system_, tab_group, *builder, this); graphics_tab = std::make_unique<ConfigureGraphics>( - system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this); + system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, + tab_group, *builder, this); input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this); - system_tab = std::make_unique<ConfigureSystem>(system_, this); + system_tab = std::make_unique<ConfigureSystem>(system_, tab_group, *builder, this); ui->setupUi(this); ui->tabWidget->addTab(addons_tab.get(), tr("Add-Ons")); - ui->tabWidget->addTab(general_tab.get(), tr("General")); ui->tabWidget->addTab(system_tab.get(), tr("System")); ui->tabWidget->addTab(cpu_tab.get(), tr("CPU")); ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics")); @@ -88,13 +91,10 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st ConfigurePerGame::~ConfigurePerGame() = default; void ConfigurePerGame::ApplyConfiguration() { + for (const auto tab : *tab_group) { + tab->ApplyConfiguration(); + } addons_tab->ApplyConfiguration(); - general_tab->ApplyConfiguration(); - cpu_tab->ApplyConfiguration(); - system_tab->ApplyConfiguration(); - graphics_tab->ApplyConfiguration(); - graphics_advanced_tab->ApplyConfiguration(); - audio_tab->ApplyConfiguration(); input_tab->ApplyConfiguration(); system.ApplySettings(); diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index 7ec1ded062..1a727f32c0 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -10,9 +10,12 @@ #include <QDialog> #include <QList> +#include "configuration/shared_widget.h" #include "core/file_sys/vfs_types.h" #include "vk_device_info.h" #include "yuzu/configuration/config.h" +#include "yuzu/configuration/configuration_shared.h" +#include "yuzu/configuration/shared_translation.h" namespace Core { class System; @@ -25,7 +28,6 @@ class InputSubsystem; class ConfigurePerGameAddons; class ConfigureAudio; class ConfigureCpu; -class ConfigureGeneral; class ConfigureGraphics; class ConfigureGraphicsAdvanced; class ConfigureInputPerGame; @@ -73,11 +75,12 @@ private: std::unique_ptr<Config> game_config; Core::System& system; + std::unique_ptr<ConfigurationShared::Builder> builder; + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> tab_group; std::unique_ptr<ConfigurePerGameAddons> addons_tab; std::unique_ptr<ConfigureAudio> audio_tab; std::unique_ptr<ConfigureCpu> cpu_tab; - std::unique_ptr<ConfigureGeneral> general_tab; std::unique_ptr<ConfigureGraphicsAdvanced> graphics_advanced_tab; std::unique_ptr<ConfigureGraphics> graphics_tab; std::unique_ptr<ConfigureInputPerGame> input_tab; diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui index 85c86e1070..99ba2fd180 100644 --- a/src/yuzu/configuration/configure_per_game.ui +++ b/src/yuzu/configuration/configure_per_game.ui @@ -2,6 +2,14 @@ <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>607</height> + </rect> + </property> <property name="minimumSize"> <size> <width>900</width> @@ -225,20 +233,31 @@ </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> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>Some settings are only available when a game is not running.</string> + </property> + </widget> + </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> </item> </layout> </widget> diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index f1ae312c65..c4833f4e70 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -3,16 +3,23 @@ #include <chrono> #include <optional> +#include <vector> +#include <QCheckBox> +#include <QComboBox> +#include <QDateTimeEdit> #include <QFileDialog> #include <QGraphicsItem> +#include <QLineEdit> #include <QMessageBox> #include "common/settings.h" #include "core/core.h" #include "core/hle/service/time/time_manager.h" #include "ui_configure_system.h" +#include "yuzu/configuration/config.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_system.h" +#include "yuzu/configuration/shared_widget.h" constexpr std::array<u32, 7> LOCALE_BLOCKLIST{ // pzzefezrpnkzeidfej @@ -37,44 +44,32 @@ static bool IsValidLocale(u32 region_index, u32 language_index) { return ((LOCALE_BLOCKLIST.at(region_index) >> language_index) & 1) == 0; } -ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent) - : QWidget(parent), ui{std::make_unique<Ui::ConfigureSystem>()}, system{system_} { +ConfigureSystem::ConfigureSystem(Core::System& system_, + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_, + const ConfigurationShared::Builder& builder, QWidget* parent) + : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureSystem>()}, system{system_} { ui->setupUi(this); - connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](int state) { - ui->rng_seed_edit->setEnabled(state == Qt::Checked); - if (state != Qt::Checked) { - ui->rng_seed_edit->setText(QStringLiteral("00000000")); - } - }); - - connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](int state) { - ui->custom_rtc_edit->setEnabled(state == Qt::Checked); - if (state != Qt::Checked) { - ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime()); - } - }); + Setup(builder); - const auto locale_check = [this](int index) { - const auto region_index = ConfigurationShared::GetComboboxIndex( - Settings::values.region_index.GetValue(true), ui->combo_region); - const auto language_index = ConfigurationShared::GetComboboxIndex( - Settings::values.language_index.GetValue(true), ui->combo_language); + const auto locale_check = [this]() { + const auto region_index = combo_region->currentIndex(); + const auto language_index = combo_language->currentIndex(); const bool valid_locale = IsValidLocale(region_index, language_index); ui->label_warn_invalid_locale->setVisible(!valid_locale); if (!valid_locale) { ui->label_warn_invalid_locale->setText( tr("Warning: \"%1\" is not a valid language for region \"%2\"") - .arg(ui->combo_language->currentText()) - .arg(ui->combo_region->currentText())); + .arg(combo_language->currentText()) + .arg(combo_region->currentText())); } }; - connect(ui->combo_language, qOverload<int>(&QComboBox::currentIndexChanged), this, - locale_check); - connect(ui->combo_region, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check); + connect(combo_language, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check); + connect(combo_region, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check); - SetupPerGameUI(); + ui->label_warn_invalid_locale->setVisible(false); + locale_check(); SetConfiguration(); } @@ -93,137 +88,66 @@ void ConfigureSystem::RetranslateUI() { ui->retranslateUi(this); } -void ConfigureSystem::SetConfiguration() { - enabled = !system.IsPoweredOn(); - const auto rng_seed = - QStringLiteral("%1") - .arg(Settings::values.rng_seed.GetValue().value_or(0), 8, 16, QLatin1Char{'0'}) - .toUpper(); - const auto rtc_time = Settings::values.custom_rtc.value_or(QDateTime::currentSecsSinceEpoch()); - - ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value()); - ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value() && - Settings::values.rng_seed.UsingGlobal()); - ui->rng_seed_edit->setText(rng_seed); - - ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); - ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); - ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); - ui->device_name_edit->setText( - QString::fromUtf8(Settings::values.device_name.GetValue().c_str())); - ui->use_unsafe_extended_memory_layout->setEnabled(enabled); - ui->use_unsafe_extended_memory_layout->setChecked( - Settings::values.use_unsafe_extended_memory_layout.GetValue()); - - if (Settings::IsConfiguringGlobal()) { - ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); - ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue()); - ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue()); - } else { - ConfigurationShared::SetPerGameSetting(ui->combo_language, - &Settings::values.language_index); - ConfigurationShared::SetPerGameSetting(ui->combo_region, &Settings::values.region_index); - ConfigurationShared::SetPerGameSetting(ui->combo_time_zone, - &Settings::values.time_zone_index); - - ConfigurationShared::SetHighlight(ui->label_language, - !Settings::values.language_index.UsingGlobal()); - ConfigurationShared::SetHighlight(ui->label_region, - !Settings::values.region_index.UsingGlobal()); - ConfigurationShared::SetHighlight(ui->label_timezone, - !Settings::values.time_zone_index.UsingGlobal()); - } -} +void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) { + auto& core_layout = *ui->core_widget->layout(); + auto& system_layout = *ui->system_widget->layout(); -void ConfigureSystem::ReadSystemSettings() {} + std::map<u32, QWidget*> core_hold{}; + std::map<u32, QWidget*> system_hold{}; -void ConfigureSystem::ApplyConfiguration() { - // Allow setting custom RTC even if system is powered on, - // to allow in-game time to be fast forwarded - if (Settings::IsConfiguringGlobal()) { - if (ui->custom_rtc_checkbox->isChecked()) { - Settings::values.custom_rtc = ui->custom_rtc_edit->dateTime().toSecsSinceEpoch(); - if (system.IsPoweredOn()) { - const s64 posix_time{*Settings::values.custom_rtc}; - system.GetTimeManager().UpdateLocalSystemClockTime(posix_time); - } - } else { - Settings::values.custom_rtc = std::nullopt; + std::vector<Settings::BasicSetting*> settings; + auto push = [&settings](auto& list) { + for (auto setting : list) { + settings.push_back(setting); } - } + }; - Settings::values.device_name = ui->device_name_edit->text().toStdString(); + push(Settings::values.linkage.by_category[Settings::Category::Core]); + push(Settings::values.linkage.by_category[Settings::Category::System]); - if (!enabled) { - return; - } + for (auto setting : settings) { + ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index, ui->combo_language); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index, - ui->combo_time_zone); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_unsafe_extended_memory_layout, - ui->use_unsafe_extended_memory_layout, - use_unsafe_extended_memory_layout); - - if (Settings::IsConfiguringGlobal()) { - // Guard if during game and set to game-specific value - if (Settings::values.rng_seed.UsingGlobal()) { - if (ui->rng_seed_checkbox->isChecked()) { - Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16)); - } else { - Settings::values.rng_seed.SetValue(std::nullopt); - } + if (widget == nullptr) { + continue; } - } else { - switch (use_rng_seed) { - case ConfigurationShared::CheckState::On: - case ConfigurationShared::CheckState::Off: - Settings::values.rng_seed.SetGlobal(false); - if (ui->rng_seed_checkbox->isChecked()) { - Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16)); - } else { - Settings::values.rng_seed.SetValue(std::nullopt); - } - break; - case ConfigurationShared::CheckState::Global: - Settings::values.rng_seed.SetGlobal(false); - Settings::values.rng_seed.SetValue(std::nullopt); - Settings::values.rng_seed.SetGlobal(true); + if (!widget->Valid()) { + widget->deleteLater(); + continue; + } + + if (setting->Id() == Settings::values.region_index.Id()) { + // Keep track of the region_index (and langauge_index) combobox to validate the selected + // settings + combo_region = widget->combobox; + } else if (setting->Id() == Settings::values.language_index.Id()) { + combo_language = widget->combobox; + } + + switch (setting->GetCategory()) { + case Settings::Category::Core: + core_hold.emplace(setting->Id(), widget); break; - case ConfigurationShared::CheckState::Count: + case Settings::Category::System: + system_hold.emplace(setting->Id(), widget); break; + default: + widget->deleteLater(); } } + for (const auto& [label, widget] : core_hold) { + core_layout.addWidget(widget); + } + for (const auto& [id, widget] : system_hold) { + system_layout.addWidget(widget); + } } -void ConfigureSystem::SetupPerGameUI() { - if (Settings::IsConfiguringGlobal()) { - ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal()); - ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal()); - ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal()); - ui->rng_seed_checkbox->setEnabled(Settings::values.rng_seed.UsingGlobal()); - ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.UsingGlobal()); +void ConfigureSystem::SetConfiguration() {} - return; +void ConfigureSystem::ApplyConfiguration() { + const bool powered_on = system.IsPoweredOn(); + for (const auto& func : apply_funcs) { + func(powered_on); } - - ConfigurationShared::SetColoredComboBox(ui->combo_language, ui->label_language, - Settings::values.language_index.GetValue(true)); - ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region, - Settings::values.region_index.GetValue(true)); - ConfigurationShared::SetColoredComboBox(ui->combo_time_zone, ui->label_timezone, - Settings::values.time_zone_index.GetValue(true)); - - ConfigurationShared::SetColoredTristate( - ui->rng_seed_checkbox, Settings::values.rng_seed.UsingGlobal(), - Settings::values.rng_seed.GetValue().has_value(), - Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed); - - ConfigurationShared::SetColoredTristate(ui->use_unsafe_extended_memory_layout, - Settings::values.use_unsafe_extended_memory_layout, - use_unsafe_extended_memory_layout); - - ui->custom_rtc_checkbox->setVisible(false); - ui->custom_rtc_edit->setVisible(false); } diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index ce1a91601c..eab99a48a7 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h @@ -3,45 +3,53 @@ #pragma once +#include <functional> #include <memory> +#include <vector> #include <QWidget> +#include "yuzu/configuration/configuration_shared.h" +class QCheckBox; +class QLineEdit; +class QComboBox; +class QDateTimeEdit; namespace Core { class System; } -namespace ConfigurationShared { -enum class CheckState; -} - namespace Ui { class ConfigureSystem; } -class ConfigureSystem : public QWidget { - Q_OBJECT +namespace ConfigurationShared { +class Builder; +} +class ConfigureSystem : public ConfigurationShared::Tab { public: - explicit ConfigureSystem(Core::System& system_, QWidget* parent = nullptr); + explicit ConfigureSystem(Core::System& system_, + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group, + const ConfigurationShared::Builder& builder, + QWidget* parent = nullptr); ~ConfigureSystem() override; - void ApplyConfiguration(); - void SetConfiguration(); + void ApplyConfiguration() override; + void SetConfiguration() override; private: void changeEvent(QEvent* event) override; void RetranslateUI(); - void ReadSystemSettings(); + void Setup(const ConfigurationShared::Builder& builder); - void SetupPerGameUI(); + std::vector<std::function<void(bool)>> apply_funcs{}; std::unique_ptr<Ui::ConfigureSystem> ui; bool enabled = false; - ConfigurationShared::CheckState use_rng_seed; - ConfigurationShared::CheckState use_unsafe_extended_memory_layout; - Core::System& system; + + QComboBox* combo_region; + QComboBox* combo_language; }; diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index e0caecd5e3..2a735836e1 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>366</width> + <width>605</width> <height>483</height> </rect> </property> @@ -22,470 +22,63 @@ <item> <widget class="QGroupBox" name="group_system_settings"> <property name="title"> - <string>System Settings</string> + <string>System</string> </property> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="1" column="0"> - <widget class="QLabel" name="label_region"> - <property name="text"> - <string>Region:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QComboBox" name="combo_time_zone"> - <item> - <property name="text"> - <string>Auto</string> - </property> - </item> - <item> - <property name="text"> - <string>Default</string> - </property> - </item> - <item> - <property name="text"> - <string>CET</string> - </property> - </item> - <item> - <property name="text"> - <string>CST6CDT</string> - </property> - </item> - <item> - <property name="text"> - <string>Cuba</string> - </property> - </item> - <item> - <property name="text"> - <string>EET</string> - </property> - </item> - <item> - <property name="text"> - <string>Egypt</string> - </property> - </item> - <item> - <property name="text"> - <string>Eire</string> - </property> - </item> - <item> - <property name="text"> - <string>EST</string> - </property> - </item> - <item> - <property name="text"> - <string>EST5EDT</string> - </property> - </item> - <item> - <property name="text"> - <string>GB</string> - </property> - </item> - <item> - <property name="text"> - <string>GB-Eire</string> - </property> - </item> - <item> - <property name="text"> - <string>GMT</string> - </property> - </item> - <item> - <property name="text"> - <string>GMT+0</string> - </property> - </item> - <item> - <property name="text"> - <string>GMT-0</string> - </property> - </item> - <item> - <property name="text"> - <string>GMT0</string> - </property> - </item> - <item> - <property name="text"> - <string>Greenwich</string> - </property> - </item> - <item> - <property name="text"> - <string>Hongkong</string> - </property> - </item> - <item> - <property name="text"> - <string>HST</string> - </property> - </item> - <item> - <property name="text"> - <string>Iceland</string> - </property> - </item> - <item> - <property name="text"> - <string>Iran</string> - </property> - </item> - <item> - <property name="text"> - <string>Israel</string> - </property> - </item> - <item> - <property name="text"> - <string>Jamaica</string> - </property> - </item> - <item> - <property name="text"> - <string>Japan</string> - </property> - </item> - <item> - <property name="text"> - <string>Kwajalein</string> - </property> - </item> - <item> - <property name="text"> - <string>Libya</string> - </property> - </item> - <item> - <property name="text"> - <string>MET</string> - </property> - </item> - <item> - <property name="text"> - <string>MST</string> - </property> - </item> - <item> - <property name="text"> - <string>MST7MDT</string> - </property> - </item> - <item> - <property name="text"> - <string>Navajo</string> - </property> - </item> - <item> - <property name="text"> - <string>NZ</string> - </property> - </item> - <item> - <property name="text"> - <string>NZ-CHAT</string> - </property> - </item> - <item> - <property name="text"> - <string>Poland</string> - </property> - </item> - <item> - <property name="text"> - <string>Portugal</string> - </property> - </item> - <item> - <property name="text"> - <string>PRC</string> - </property> - </item> - <item> - <property name="text"> - <string>PST8PDT</string> - </property> - </item> - <item> - <property name="text"> - <string>ROC</string> - </property> - </item> - <item> - <property name="text"> - <string>ROK</string> - </property> - </item> - <item> - <property name="text"> - <string>Singapore</string> - </property> - </item> - <item> - <property name="text"> - <string>Turkey</string> - </property> - </item> - <item> - <property name="text"> - <string>UCT</string> - </property> - </item> - <item> - <property name="text"> - <string>Universal</string> - </property> - </item> - <item> - <property name="text"> - <string>UTC</string> - </property> - </item> - <item> - <property name="text"> - <string>W-SU</string> - </property> - </item> - <item> - <property name="text"> - <string>WET</string> - </property> - </item> - <item> - <property name="text"> - <string>Zulu</string> - </property> - </item> - </widget> - </item> - <item row="1" column="1"> - <widget class="QComboBox" name="combo_region"> - <item> - <property name="text"> - <string>Japan</string> - </property> - </item> - <item> - <property name="text"> - <string>USA</string> - </property> - </item> - <item> - <property name="text"> - <string>Europe</string> - </property> - </item> - <item> - <property name="text"> - <string>Australia</string> - </property> - </item> - <item> - <property name="text"> - <string>China</string> - </property> - </item> - <item> - <property name="text"> - <string>Korea</string> - </property> - </item> - <item> - <property name="text"> - <string>Taiwan</string> - </property> - </item> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_timezone"> - <property name="text"> - <string>Time Zone:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QComboBox" name="combo_language"> - <property name="toolTip"> - <string>Note: this can be overridden when region setting is auto-select</string> - </property> - <item> - <property name="text"> - <string>Japanese (日本語)</string> - </property> - </item> - <item> - <property name="text"> - <string>American English</string> - </property> - </item> - <item> - <property name="text"> - <string>French (français)</string> - </property> - </item> - <item> - <property name="text"> - <string>German (Deutsch)</string> - </property> - </item> - <item> - <property name="text"> - <string>Italian (italiano)</string> - </property> - </item> - <item> - <property name="text"> - <string>Spanish (español)</string> - </property> - </item> - <item> - <property name="text"> - <string>Chinese</string> - </property> - </item> - <item> - <property name="text"> - <string>Korean (한국어)</string> - </property> - </item> - <item> - <property name="text"> - <string>Dutch (Nederlands)</string> - </property> - </item> - <item> - <property name="text"> - <string>Portuguese (português)</string> - </property> - </item> - <item> - <property name="text"> - <string>Russian (Русский)</string> - </property> - </item> - <item> - <property name="text"> - <string>Taiwanese</string> - </property> - </item> - <item> - <property name="text"> - <string>British English</string> - </property> - </item> - <item> - <property name="text"> - <string>Canadian French</string> - </property> - </item> - <item> - <property name="text"> - <string>Latin American Spanish</string> - </property> - </item> - <item> - <property name="text"> - <string>Simplified Chinese</string> - </property> - </item> - <item> - <property name="text"> - <string>Traditional Chinese (正體中文)</string> - </property> - </item> - <item> - <property name="text"> - <string>Brazilian Portuguese (português do Brasil)</string> - </property> - </item> - </widget> - </item> - <item row="4" column="0"> - <widget class="QCheckBox" name="custom_rtc_checkbox"> - <property name="text"> - <string>Custom RTC</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_language"> - <property name="text"> - <string>Language</string> - </property> - </widget> - </item> - <item row="5" column="0"> - <widget class="QCheckBox" name="rng_seed_checkbox"> - <property name="text"> - <string>RNG Seed</string> - </property> - </widget> - </item> - <item row="6" column="0"> - <widget class="QLabel" name="device_name_label"> - <property name="text"> - <string>Device Name</string> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="QDateTimeEdit" name="custom_rtc_edit"> - <property name="minimumDate"> - <date> - <year>1970</year> - <month>1</month> - <day>1</day> - </date> - </property> - </widget> - </item> - <item row="6" column="1"> - <widget class="QLineEdit" name="device_name_edit"> - <property name="maxLength"> - <number>128</number> - </property> - </widget> - </item> - <item row="5" column="1"> - <widget class="QLineEdit" name="rng_seed_edit"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <family>Lucida Console</family> - </font> - </property> - <property name="inputMask"> - <string notr="true">HHHHHHHH</string> - </property> - <property name="maxLength"> - <number>8</number> - </property> - </widget> - </item> - <item row="7" column="0"> - <widget class="QCheckBox" name="use_unsafe_extended_memory_layout"> - <property name="text"> - <string>Unsafe extended memory layout (8GB DRAM)</string> - </property> - </widget> - </item> - </layout> + <widget class="QWidget" name="system_widget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <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> + </layout> + </widget> + </item> + <item> + <widget class="QLabel" name="label_warn_invalid_locale"> + <property name="text"> + <string/> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Core</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <widget class="QWidget" name="core_widget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <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> + </layout> + </widget> </item> </layout> </widget> @@ -503,26 +96,6 @@ </property> </spacer> </item> - <item> - <widget class="QLabel" name="label_warn_invalid_locale"> - <property name="text"> - <string></string> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_disable_info"> - <property name="text"> - <string>System settings are available only when game is not running.</string> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> </layout> </item> </layout> diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp new file mode 100644 index 0000000000..335810788a --- /dev/null +++ b/src/yuzu/configuration/shared_translation.cpp @@ -0,0 +1,388 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/time_zone.h" +#include "yuzu/configuration/shared_translation.h" + +#include <map> +#include <memory> +#include <tuple> +#include <utility> +#include <QWidget> +#include "common/settings.h" +#include "common/settings_enums.h" +#include "common/settings_setting.h" +#include "yuzu/uisettings.h" + +namespace ConfigurationShared { + +std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { + std::unique_ptr<TranslationMap> translations = std::make_unique<TranslationMap>(); + const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); }; + +#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \ + translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{tr((NAME)), tr((TOOLTIP))}}) + + // A setting can be ignored by giving it a blank name + + // Audio + INSERT(Settings, sink_id, "Output Engine:", ""); + INSERT(Settings, audio_output_device_id, "Output Device:", ""); + INSERT(Settings, audio_input_device_id, "Input Device:", ""); + INSERT(Settings, audio_muted, "Mute audio when in background", ""); + INSERT(Settings, volume, "Volume:", ""); + INSERT(Settings, dump_audio_commands, "", ""); + + // Core + INSERT(Settings, use_multi_core, "Multicore CPU Emulation", ""); + INSERT(Settings, memory_layout_mode, "Memory Layout", ""); + INSERT(Settings, use_speed_limit, "", ""); + INSERT(Settings, speed_limit, "Limit Speed Percent", ""); + + // Cpu + INSERT(Settings, cpu_accuracy, "Accuracy:", ""); + + // Cpu Debug + + // Cpu Unsafe + INSERT(Settings, cpuopt_unsafe_unfuse_fma, + "Unfuse FMA (improve performance on CPUs without FMA)", + "This option improves speed by reducing accuracy of fused-multiply-add instructions on " + "CPUs without native FMA support."); + INSERT(Settings, cpuopt_unsafe_reduce_fp_error, "Faster FRSQRTE and FRECPE", + "This option improves the speed of some approximate floating-point functions by using " + "less accurate native approximations."); + INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, "Faster ASIMD instructions (32 bits only)", + "This option improves the speed of 32 bits ASIMD floating-point functions by running " + "with incorrect rounding modes."); + INSERT(Settings, cpuopt_unsafe_inaccurate_nan, "Inaccurate NaN handling", + "This option improves speed by removing NaN checking. Please note this also reduces " + "accuracy of certain floating-point instructions."); + INSERT( + Settings, cpuopt_unsafe_fastmem_check, "Disable address space checks", + "This option improves speed by eliminating a safety check before every memory read/write " + "in guest. Disabling it may allow a game to read/write the emulator's memory."); + INSERT(Settings, cpuopt_unsafe_ignore_global_monitor, "Ignore global monitor", + "This option improves speed by relying only on the semantics of cmpxchg to ensure " + "safety of exclusive access instructions. Please note this may result in deadlocks and " + "other race conditions."); + + // Renderer + INSERT(Settings, renderer_backend, "API:", ""); + INSERT(Settings, vulkan_device, "Device:", ""); + INSERT(Settings, shader_backend, "Shader Backend:", ""); + INSERT(Settings, resolution_setup, "Resolution:", ""); + INSERT(Settings, scaling_filter, "Window Adapting Filter:", ""); + INSERT(Settings, fsr_sharpening_slider, "FSR Sharpness:", ""); + INSERT(Settings, anti_aliasing, "Anti-Aliasing Method:", ""); + INSERT(Settings, fullscreen_mode, "Fullscreen Mode:", ""); + INSERT(Settings, aspect_ratio, "Aspect Ratio:", ""); + INSERT(Settings, use_disk_shader_cache, "Use disk pipeline cache", ""); + INSERT(Settings, use_asynchronous_gpu_emulation, "Use asynchronous GPU emulation", ""); + INSERT(Settings, nvdec_emulation, "NVDEC emulation:", ""); + INSERT(Settings, accelerate_astc, "ASTC Decoding Method:", ""); + INSERT(Settings, astc_recompression, "ASTC Recompression Method:", ""); + INSERT(Settings, vsync_mode, "VSync Mode:", + "FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " + "refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from " + "a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop " + "frames.\nImmediate (no synchronization) just presents whatever is available and can " + "exhibit tearing."); + INSERT(Settings, bg_red, "", ""); + INSERT(Settings, bg_green, "", ""); + INSERT(Settings, bg_blue, "", ""); + + // Renderer (Advanced Graphics) + INSERT(Settings, async_presentation, "Enable asynchronous presentation (Vulkan only)", ""); + INSERT(Settings, renderer_force_max_clock, "Force maximum clocks (Vulkan only)", + "Runs work in the background while waiting for graphics commands to keep the GPU from " + "lowering its clock speed."); + INSERT(Settings, max_anisotropy, "Anisotropic Filtering:", ""); + INSERT(Settings, gpu_accuracy, "Accuracy Level:", ""); + INSERT(Settings, use_asynchronous_shaders, "Use asynchronous shader building (Hack)", + "Enables asynchronous shader compilation, which may reduce shader stutter. This feature " + "is experimental."); + INSERT(Settings, use_fast_gpu_time, "Use Fast GPU Time (Hack)", + "Enables Fast GPU Time. This option will force most games to run at their highest " + "native resolution."); + INSERT(Settings, use_vulkan_driver_pipeline_cache, "Use Vulkan pipeline cache", + "Enables GPU vendor-specific pipeline cache. This option can improve shader loading " + "time significantly in cases where the Vulkan driver does not store pipeline cache " + "files internally."); + INSERT(Settings, enable_compute_pipelines, "Enable Compute Pipelines (Intel Vulkan Only)", + "Enable compute pipelines, required by some games.\nThis setting only exists for Intel " + "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled " + "on all other drivers."); + INSERT(Settings, use_reactive_flushing, "Enable Reactive Flushing", + "Uses reactive flushing instead of predictive flushing, allowing more accurate memory " + "syncing."); + INSERT(Settings, use_video_framerate, "Sync to framerate of video playback", + "Run the game at normal speed during video playback, even when the framerate is " + "unlocked."); + INSERT(Settings, barrier_feedback_loops, "Barrier feedback loops", + "Improves rendering of transparency effects in specific games."); + + // Renderer (Debug) + + // System + INSERT(Settings, rng_seed, "RNG Seed", ""); + INSERT(Settings, rng_seed_enabled, "", ""); + INSERT(Settings, device_name, "Device Name", ""); + INSERT(Settings, custom_rtc, "Custom RTC", ""); + INSERT(Settings, custom_rtc_enabled, "", ""); + INSERT(Settings, language_index, + "Language:", "Note: this can be overridden when region setting is auto-select"); + INSERT(Settings, region_index, "Region:", ""); + INSERT(Settings, time_zone_index, "Time Zone:", ""); + INSERT(Settings, sound_index, "Sound Output Mode:", ""); + INSERT(Settings, use_docked_mode, "", ""); + INSERT(Settings, current_user, "", ""); + + // Controls + + // Data Storage + + // Debugging + + // Debugging Graphics + + // Network + + // Web Service + + // Ui + + // Ui General + INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); + INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); + INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", ""); + INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); + INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); + + // Ui Debugging + + // Ui Multiplayer + + // Ui Games list + +#undef INSERT + + return translations; +} + +std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) { + std::unique_ptr<ComboboxTranslationMap> translations = + std::make_unique<ComboboxTranslationMap>(); + const auto& tr = [&](const char* text, const char* context = "") { + return parent->tr(text, context); + }; + +#define PAIR(ENUM, VALUE, TRANSLATION) \ + { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION) } +#define CTX_PAIR(ENUM, VALUE, TRANSLATION, CONTEXT) \ + { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION, CONTEXT) } + + // Intentionally skipping VSyncMode to let the UI fill that one out + + translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), + { + PAIR(AstcDecodeMode, Cpu, "CPU"), + PAIR(AstcDecodeMode, Gpu, "GPU"), + PAIR(AstcDecodeMode, CpuAsynchronous, "CPU Asynchronous"), + }}); + translations->insert({Settings::EnumMetadata<Settings::AstcRecompression>::Index(), + { + PAIR(AstcRecompression, Uncompressed, "Uncompressed (Best quality)"), + PAIR(AstcRecompression, Bc1, "BC1 (Low quality)"), + PAIR(AstcRecompression, Bc3, "BC3 (Medium quality)"), + }}); + translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), + { +#ifdef HAS_OPENGL + PAIR(RendererBackend, OpenGL, "OpenGL"), +#endif + PAIR(RendererBackend, Vulkan, "Vulkan"), + PAIR(RendererBackend, Null, "Null"), + }}); + translations->insert({Settings::EnumMetadata<Settings::ShaderBackend>::Index(), + { + PAIR(ShaderBackend, Glsl, "GLSL"), + PAIR(ShaderBackend, Glasm, "GLASM (Assembly Shaders, NVIDIA Only)"), + PAIR(ShaderBackend, SpirV, "SPIR-V (Experimental, Mesa Only)"), + }}); + translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(), + { + PAIR(GpuAccuracy, Normal, "Normal"), + PAIR(GpuAccuracy, High, "High"), + PAIR(GpuAccuracy, Extreme, "Extreme"), + }}); + translations->insert({Settings::EnumMetadata<Settings::CpuAccuracy>::Index(), + { + PAIR(CpuAccuracy, Auto, "Auto"), + PAIR(CpuAccuracy, Accurate, "Accurate"), + PAIR(CpuAccuracy, Unsafe, "Unsafe"), + PAIR(CpuAccuracy, Paranoid, "Paranoid (disables most optimizations)"), + }}); + translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(), + { + PAIR(FullscreenMode, Borderless, "Borderless Windowed"), + PAIR(FullscreenMode, Exclusive, "Exclusive Fullscreen"), + }}); + translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(), + { + PAIR(NvdecEmulation, Off, "No Video Output"), + PAIR(NvdecEmulation, Cpu, "CPU Video Decoding"), + PAIR(NvdecEmulation, Gpu, "GPU Video Decoding (Default)"), + }}); + translations->insert({Settings::EnumMetadata<Settings::ResolutionSetup>::Index(), + { + PAIR(ResolutionSetup, Res1_2X, "0.5X (360p/540p) [EXPERIMENTAL]"), + PAIR(ResolutionSetup, Res3_4X, "0.75X (540p/810p) [EXPERIMENTAL]"), + PAIR(ResolutionSetup, Res1X, "1X (720p/1080p)"), + PAIR(ResolutionSetup, Res3_2X, "1.5X (1080p/1620p) [EXPERIMENTAL]"), + PAIR(ResolutionSetup, Res2X, "2X (1440p/2160p)"), + PAIR(ResolutionSetup, Res3X, "3X (2160p/3240p)"), + PAIR(ResolutionSetup, Res4X, "4X (2880p/4320p)"), + PAIR(ResolutionSetup, Res5X, "5X (3600p/5400p)"), + PAIR(ResolutionSetup, Res6X, "6X (4320p/6480p)"), + PAIR(ResolutionSetup, Res7X, "7X (5040p/7560p)"), + PAIR(ResolutionSetup, Res8X, "8X (5760p/8640p)"), + }}); + translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(), + { + PAIR(ScalingFilter, NearestNeighbor, "Nearest Neighbor"), + PAIR(ScalingFilter, Bilinear, "Bilinear"), + PAIR(ScalingFilter, Bicubic, "Bicubic"), + PAIR(ScalingFilter, Gaussian, "Gaussian"), + PAIR(ScalingFilter, ScaleForce, "ScaleForce"), + PAIR(ScalingFilter, Fsr, "AMD FidelityFX™️ Super Resolution"), + }}); + translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(), + { + PAIR(AntiAliasing, None, "None"), + PAIR(AntiAliasing, Fxaa, "FXAA"), + PAIR(AntiAliasing, Smaa, "SMAA"), + }}); + translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(), + { + PAIR(AspectRatio, R16_9, "Default (16:9)"), + PAIR(AspectRatio, R4_3, "Force 4:3"), + PAIR(AspectRatio, R21_9, "Force 21:9"), + PAIR(AspectRatio, R16_10, "Force 16:10"), + PAIR(AspectRatio, Stretch, "Stretch to Window"), + }}); + translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(), + { + PAIR(AnisotropyMode, Automatic, "Automatic"), + PAIR(AnisotropyMode, Default, "Default"), + PAIR(AnisotropyMode, X2, "2x"), + PAIR(AnisotropyMode, X4, "4x"), + PAIR(AnisotropyMode, X8, "8x"), + PAIR(AnisotropyMode, X16, "16x"), + }}); + translations->insert( + {Settings::EnumMetadata<Settings::Language>::Index(), + { + PAIR(Language, Japanese, "Japanese (日本語)"), + PAIR(Language, EnglishAmerican, "American English"), + PAIR(Language, French, "French (français)"), + PAIR(Language, German, "German (Deutsch)"), + PAIR(Language, Italian, "Italian (italiano)"), + PAIR(Language, Spanish, "Spanish (español)"), + PAIR(Language, Chinese, "Chinese"), + PAIR(Language, Korean, "Korean (한국어)"), + PAIR(Language, Dutch, "Dutch (Nederlands)"), + PAIR(Language, Portuguese, "Portuguese (português)"), + PAIR(Language, Russian, "Russian (Русский)"), + PAIR(Language, Taiwanese, "Taiwanese"), + PAIR(Language, EnglishBritish, "British English"), + PAIR(Language, FrenchCanadian, "Canadian French"), + PAIR(Language, SpanishLatin, "Latin American Spanish"), + PAIR(Language, ChineseSimplified, "Simplified Chinese"), + PAIR(Language, ChineseTraditional, "Traditional Chinese (正體中文)"), + PAIR(Language, PortugueseBrazilian, "Brazilian Portuguese (português do Brasil)"), + }}); + translations->insert({Settings::EnumMetadata<Settings::Region>::Index(), + { + PAIR(Region, Japan, "Japan"), + PAIR(Region, Usa, "USA"), + PAIR(Region, Europe, "Europe"), + PAIR(Region, Australia, "Australia"), + PAIR(Region, China, "China"), + PAIR(Region, Korea, "Korea"), + PAIR(Region, Taiwan, "Taiwan"), + }}); + translations->insert( + {Settings::EnumMetadata<Settings::TimeZone>::Index(), + { + {static_cast<u32>(Settings::TimeZone::Auto), + tr("Auto (%1)", "Auto select time zone") + .arg(QString::fromStdString( + Settings::GetTimeZoneString(Settings::TimeZone::Auto)))}, + {static_cast<u32>(Settings::TimeZone::Default), + tr("Default (%1)", "Default time zone") + .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, + PAIR(TimeZone, Cet, "CET"), + PAIR(TimeZone, Cst6Cdt, "CST6CDT"), + PAIR(TimeZone, Cuba, "Cuba"), + PAIR(TimeZone, Eet, "EET"), + PAIR(TimeZone, Egypt, "Egypt"), + PAIR(TimeZone, Eire, "Eire"), + PAIR(TimeZone, Est, "EST"), + PAIR(TimeZone, Est5Edt, "EST5EDT"), + PAIR(TimeZone, Gb, "GB"), + PAIR(TimeZone, GbEire, "GB-Eire"), + PAIR(TimeZone, Gmt, "GMT"), + PAIR(TimeZone, GmtPlusZero, "GMT+0"), + PAIR(TimeZone, GmtMinusZero, "GMT-0"), + PAIR(TimeZone, GmtZero, "GMT0"), + PAIR(TimeZone, Greenwich, "Greenwich"), + PAIR(TimeZone, Hongkong, "Hongkong"), + PAIR(TimeZone, Hst, "HST"), + PAIR(TimeZone, Iceland, "Iceland"), + PAIR(TimeZone, Iran, "Iran"), + PAIR(TimeZone, Israel, "Israel"), + PAIR(TimeZone, Jamaica, "Jamaica"), + PAIR(TimeZone, Japan, "Japan"), + PAIR(TimeZone, Kwajalein, "Kwajalein"), + PAIR(TimeZone, Libya, "Libya"), + PAIR(TimeZone, Met, "MET"), + PAIR(TimeZone, Mst, "MST"), + PAIR(TimeZone, Mst7Mdt, "MST7MDT"), + PAIR(TimeZone, Navajo, "Navajo"), + PAIR(TimeZone, Nz, "NZ"), + PAIR(TimeZone, NzChat, "NZ-CHAT"), + PAIR(TimeZone, Poland, "Poland"), + PAIR(TimeZone, Portugal, "Portugal"), + PAIR(TimeZone, Prc, "PRC"), + PAIR(TimeZone, Pst8Pdt, "PST8PDT"), + PAIR(TimeZone, Roc, "ROC"), + PAIR(TimeZone, Rok, "ROK"), + PAIR(TimeZone, Singapore, "Singapore"), + PAIR(TimeZone, Turkey, "Turkey"), + PAIR(TimeZone, Uct, "UCT"), + PAIR(TimeZone, Universal, "Universal"), + PAIR(TimeZone, Utc, "UTC"), + PAIR(TimeZone, WSu, "W-SU"), + PAIR(TimeZone, Wet, "WET"), + PAIR(TimeZone, Zulu, "Zulu"), + }}); + translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(), + { + PAIR(AudioMode, Mono, "Mono"), + PAIR(AudioMode, Stereo, "Stereo"), + PAIR(AudioMode, Surround, "Surround"), + }}); + translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(), + { + PAIR(MemoryLayout, Memory_4Gb, "4GB DRAM (Default)"), + PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), + PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), + }}); + +#undef PAIR +#undef CTX_PAIR + + return translations; +} +} // namespace ConfigurationShared diff --git a/src/yuzu/configuration/shared_translation.h b/src/yuzu/configuration/shared_translation.h new file mode 100644 index 0000000000..99a0e808ca --- /dev/null +++ b/src/yuzu/configuration/shared_translation.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <map> +#include <memory> +#include <typeindex> +#include <utility> +#include <vector> +#include <QString> +#include "common/common_types.h" + +class QWidget; + +namespace ConfigurationShared { +using TranslationMap = std::map<u32, std::pair<QString, QString>>; +using ComboboxTranslations = std::vector<std::pair<u32, QString>>; +using ComboboxTranslationMap = std::map<u32, ComboboxTranslations>; + +std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent); + +std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent); + +} // namespace ConfigurationShared diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp new file mode 100644 index 0000000000..410fa80cd4 --- /dev/null +++ b/src/yuzu/configuration/shared_widget.cpp @@ -0,0 +1,640 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "yuzu/configuration/shared_widget.h" + +#include <functional> +#include <limits> +#include <typeindex> +#include <typeinfo> +#include <utility> +#include <vector> + +#include <QAbstractButton> +#include <QAbstractSlider> +#include <QBoxLayout> +#include <QCheckBox> +#include <QComboBox> +#include <QDateTime> +#include <QDateTimeEdit> +#include <QIcon> +#include <QLabel> +#include <QLayout> +#include <QLineEdit> +#include <QObject> +#include <QPushButton> +#include <QRegularExpression> +#include <QSizePolicy> +#include <QSlider> +#include <QSpinBox> +#include <QStyle> +#include <QValidator> +#include <QVariant> +#include <QtCore/qglobal.h> +#include <QtCore/qobjectdefs.h> +#include <fmt/core.h> +#include <qglobal.h> +#include <qnamespace.h> + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/settings_common.h" +#include "yuzu/configuration/shared_translation.h" + +namespace ConfigurationShared { + +static int restore_button_count = 0; + +static std::string RelevantDefault(const Settings::BasicSetting& setting) { + return Settings::IsConfiguringGlobal() ? setting.DefaultToString() : setting.ToStringGlobal(); +} + +static QString DefaultSuffix(QWidget* parent, Settings::BasicSetting& setting) { + const auto tr = [parent](const char* text, const char* context) { + return parent->tr(text, context); + }; + + if ((setting.Specialization() & Settings::SpecializationAttributeMask) == + Settings::Specialization::Percentage) { + std::string context{fmt::format("{} percentage (e.g. 50%)", setting.GetLabel())}; + return tr("%", context.c_str()); + } + + return QStringLiteral(""); +} + +QPushButton* Widget::CreateRestoreGlobalButton(bool using_global, QWidget* parent) { + restore_button_count++; + + QStyle* style = parent->style(); + QIcon* icon = new QIcon(style->standardIcon(QStyle::SP_LineEditClearButton)); + QPushButton* restore_button = new QPushButton(*icon, QStringLiteral(""), parent); + restore_button->setObjectName(QStringLiteral("RestoreButton%1").arg(restore_button_count)); + restore_button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + // Workaround for dark theme causing min-width to be much larger than 0 + restore_button->setStyleSheet( + QStringLiteral("QAbstractButton#%1 { min-width: 0px }").arg(restore_button->objectName())); + + QSizePolicy sp_retain = restore_button->sizePolicy(); + sp_retain.setRetainSizeWhenHidden(true); + restore_button->setSizePolicy(sp_retain); + + restore_button->setEnabled(!using_global); + restore_button->setVisible(!using_global); + + return restore_button; +} + +QLabel* Widget::CreateLabel(const QString& text) { + QLabel* qt_label = new QLabel(text, this->parent); + qt_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + return qt_label; +} + +QWidget* Widget::CreateCheckBox(Settings::BasicSetting* bool_setting, const QString& label, + std::function<std::string()>& serializer, + std::function<void()>& restore_func, + const std::function<void()>& touch) { + checkbox = new QCheckBox(label, this); + checkbox->setCheckState(bool_setting->ToString() == "true" ? Qt::CheckState::Checked + : Qt::CheckState::Unchecked); + checkbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + if (!bool_setting->Save() && !Settings::IsConfiguringGlobal() && runtime_lock) { + checkbox->setEnabled(false); + } + + serializer = [this]() { + return checkbox->checkState() == Qt::CheckState::Checked ? "true" : "false"; + }; + + restore_func = [this, bool_setting]() { + checkbox->setCheckState(RelevantDefault(*bool_setting) == "true" ? Qt::Checked + : Qt::Unchecked); + }; + + if (!Settings::IsConfiguringGlobal()) { + QObject::connect(checkbox, &QCheckBox::clicked, [touch]() { touch(); }); + } + + return checkbox; +} + +QWidget* Widget::CreateCombobox(std::function<std::string()>& serializer, + std::function<void()>& restore_func, + const std::function<void()>& touch) { + const auto type = setting.EnumIndex(); + + combobox = new QComboBox(this); + combobox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + const ComboboxTranslations* enumeration{nullptr}; + if (combobox_enumerations.contains(type)) { + enumeration = &combobox_enumerations.at(type); + for (const auto& [id, name] : *enumeration) { + combobox->addItem(name); + } + } else { + return combobox; + } + + const auto find_index = [=](u32 value) -> int { + for (u32 i = 0; i < enumeration->size(); i++) { + if (enumeration->at(i).first == value) { + return i; + } + } + return -1; + }; + + const u32 setting_value = std::stoi(setting.ToString()); + combobox->setCurrentIndex(find_index(setting_value)); + + serializer = [this, enumeration]() { + int current = combobox->currentIndex(); + return std::to_string(enumeration->at(current).first); + }; + + restore_func = [this, find_index]() { + const u32 global_value = std::stoi(RelevantDefault(setting)); + combobox->setCurrentIndex(find_index(global_value)); + }; + + if (!Settings::IsConfiguringGlobal()) { + QObject::connect(combobox, QOverload<int>::of(&QComboBox::activated), + [touch]() { touch(); }); + } + + return combobox; +} + +QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer, + std::function<void()>& restore_func, + const std::function<void()>& touch, bool managed) { + const QString text = QString::fromStdString(setting.ToString()); + line_edit = new QLineEdit(this); + line_edit->setText(text); + + serializer = [this]() { return line_edit->text().toStdString(); }; + + if (!managed) { + return line_edit; + } + + restore_func = [this]() { + line_edit->setText(QString::fromStdString(RelevantDefault(setting))); + }; + + if (!Settings::IsConfiguringGlobal()) { + QObject::connect(line_edit, &QLineEdit::textChanged, [touch]() { touch(); }); + } + + return line_edit; +} + +QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& given_suffix, + std::function<std::string()>& serializer, + std::function<void()>& restore_func, + const std::function<void()>& touch) { + if (!setting.Ranged()) { + LOG_ERROR(Frontend, "\"{}\" is not a ranged setting, but a slider was requested.", + setting.GetLabel()); + return nullptr; + } + + QWidget* container = new QWidget(this); + QHBoxLayout* layout = new QHBoxLayout(container); + + slider = new QSlider(Qt::Horizontal, this); + QLabel* feedback = new QLabel(this); + + layout->addWidget(slider); + layout->addWidget(feedback); + + container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + layout->setContentsMargins(0, 0, 0, 0); + + int max_val = std::stoi(setting.MaxVal()); + + QString suffix = + given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix; + + const QString use_format = QStringLiteral("%1").append(suffix); + + QObject::connect(slider, &QAbstractSlider::valueChanged, [=](int value) { + int present = (reversed ? max_val - value : value) * multiplier + 0.5f; + feedback->setText(use_format.arg(QVariant::fromValue(present).value<QString>())); + }); + + slider->setMinimum(std::stoi(setting.MinVal())); + slider->setMaximum(max_val); + slider->setValue(std::stoi(setting.ToString())); + + slider->setInvertedAppearance(reversed); + + serializer = [this]() { return std::to_string(slider->value()); }; + restore_func = [this]() { slider->setValue(std::stoi(RelevantDefault(setting))); }; + + if (!Settings::IsConfiguringGlobal()) { + QObject::connect(slider, &QAbstractSlider::actionTriggered, [touch]() { touch(); }); + } + + return container; +} + +QWidget* Widget::CreateSpinBox(const QString& given_suffix, + std::function<std::string()>& serializer, + std::function<void()>& restore_func, + const std::function<void()>& touch) { + const int min_val = + setting.Ranged() ? std::stoi(setting.MinVal()) : std::numeric_limits<int>::min(); + const int max_val = + setting.Ranged() ? std::stoi(setting.MaxVal()) : std::numeric_limits<int>::max(); + const int default_val = std::stoi(setting.ToString()); + + QString suffix = + given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix; + + spinbox = new QSpinBox(this); + spinbox->setRange(min_val, max_val); + spinbox->setValue(default_val); + spinbox->setSuffix(suffix); + spinbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + serializer = [this]() { return std::to_string(spinbox->value()); }; + + restore_func = [this]() { + auto value{std::stol(RelevantDefault(setting))}; + spinbox->setValue(value); + }; + + if (!Settings::IsConfiguringGlobal()) { + QObject::connect(spinbox, QOverload<int>::of(&QSpinBox::valueChanged), [this, touch]() { + if (spinbox->value() != std::stoi(setting.ToStringGlobal())) { + touch(); + } + }); + } + + return spinbox; +} + +QWidget* Widget::CreateHexEdit(std::function<std::string()>& serializer, + std::function<void()>& restore_func, + const std::function<void()>& touch) { + auto* data_component = CreateLineEdit(serializer, restore_func, touch, false); + if (data_component == nullptr) { + return nullptr; + } + + auto to_hex = [=](const std::string& input) { + return QString::fromStdString(fmt::format("{:08x}", std::stoul(input))); + }; + + QRegularExpressionValidator* regex = new QRegularExpressionValidator( + QRegularExpression{QStringLiteral("^[0-9a-fA-F]{0,8}$")}, line_edit); + + const QString default_val = to_hex(setting.ToString()); + + line_edit->setText(default_val); + line_edit->setMaxLength(8); + line_edit->setValidator(regex); + + auto hex_to_dec = [this]() -> std::string { + return std::to_string(std::stoul(line_edit->text().toStdString(), nullptr, 16)); + }; + + serializer = [hex_to_dec]() { return hex_to_dec(); }; + + restore_func = [this, to_hex]() { line_edit->setText(to_hex(RelevantDefault(setting))); }; + + if (!Settings::IsConfiguringGlobal()) { + + QObject::connect(line_edit, &QLineEdit::textChanged, [touch]() { touch(); }); + } + + return line_edit; +} + +QWidget* Widget::CreateDateTimeEdit(bool disabled, bool restrict, + std::function<std::string()>& serializer, + std::function<void()>& restore_func, + const std::function<void()>& touch) { + const long long current_time = QDateTime::currentSecsSinceEpoch(); + const s64 the_time = disabled ? current_time : std::stoll(setting.ToString()); + const auto default_val = QDateTime::fromSecsSinceEpoch(the_time); + + date_time_edit = new QDateTimeEdit(this); + date_time_edit->setDateTime(default_val); + date_time_edit->setMinimumDateTime(QDateTime::fromSecsSinceEpoch(0)); + date_time_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + serializer = [this]() { return std::to_string(date_time_edit->dateTime().toSecsSinceEpoch()); }; + + auto get_clear_val = [this, restrict, current_time]() { + return QDateTime::fromSecsSinceEpoch([this, restrict, current_time]() { + if (restrict && checkbox->checkState() == Qt::Checked) { + return std::stoll(RelevantDefault(setting)); + } + return current_time; + }()); + }; + + restore_func = [this, get_clear_val]() { date_time_edit->setDateTime(get_clear_val()); }; + + if (!Settings::IsConfiguringGlobal()) { + QObject::connect(date_time_edit, &QDateTimeEdit::editingFinished, + [this, get_clear_val, touch]() { + if (date_time_edit->dateTime() != get_clear_val()) { + touch(); + } + }); + } + + return date_time_edit; +} + +void Widget::SetupComponent(const QString& label, std::function<void()>& load_func, bool managed, + RequestType request, float multiplier, + Settings::BasicSetting* other_setting, const QString& suffix) { + created = true; + const auto type = setting.TypeId(); + + QLayout* layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + + if (other_setting == nullptr) { + other_setting = setting.PairedSetting(); + } + + const bool require_checkbox = + other_setting != nullptr && other_setting->TypeId() == typeid(bool); + + if (other_setting != nullptr && other_setting->TypeId() != typeid(bool)) { + LOG_WARNING( + Frontend, + "Extra setting \"{}\" specified but is not bool, refusing to create checkbox for it.", + other_setting->GetLabel()); + } + + std::function<std::string()> checkbox_serializer = []() -> std::string { return {}; }; + std::function<void()> checkbox_restore_func = []() {}; + + std::function<void()> touch = []() {}; + std::function<std::string()> serializer = []() -> std::string { return {}; }; + std::function<void()> restore_func = []() {}; + + QWidget* data_component{nullptr}; + + request = [&]() { + if (request != RequestType::Default) { + return request; + } + switch (setting.Specialization() & Settings::SpecializationTypeMask) { + case Settings::Specialization::Default: + return RequestType::Default; + case Settings::Specialization::Time: + return RequestType::DateTimeEdit; + case Settings::Specialization::Hex: + return RequestType::HexEdit; + case Settings::Specialization::RuntimeList: + managed = false; + [[fallthrough]]; + case Settings::Specialization::List: + return RequestType::ComboBox; + case Settings::Specialization::Scalar: + return RequestType::Slider; + case Settings::Specialization::Countable: + return RequestType::SpinBox; + default: + break; + } + return request; + }(); + + if (!Settings::IsConfiguringGlobal() && managed) { + restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this); + + touch = [this]() { + LOG_DEBUG(Frontend, "Enabling custom setting for \"{}\"", setting.GetLabel()); + restore_button->setEnabled(true); + restore_button->setVisible(true); + }; + } + + if (require_checkbox) { + QWidget* lhs = + CreateCheckBox(other_setting, label, checkbox_serializer, checkbox_restore_func, touch); + layout->addWidget(lhs); + } else if (setting.TypeId() != typeid(bool)) { + QLabel* qt_label = CreateLabel(label); + layout->addWidget(qt_label); + } + + if (setting.TypeId() == typeid(bool)) { + data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch); + } else if (setting.IsEnum()) { + data_component = CreateCombobox(serializer, restore_func, touch); + } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) || + type == typeid(s64) || type == typeid(u8)) { + switch (request) { + case RequestType::Slider: + case RequestType::ReverseSlider: + data_component = CreateSlider(request == RequestType::ReverseSlider, multiplier, suffix, + serializer, restore_func, touch); + break; + case RequestType::Default: + case RequestType::LineEdit: + data_component = CreateLineEdit(serializer, restore_func, touch); + break; + case RequestType::DateTimeEdit: + data_component = CreateDateTimeEdit(other_setting->ToString() != "true", true, + serializer, restore_func, touch); + break; + case RequestType::SpinBox: + data_component = CreateSpinBox(suffix, serializer, restore_func, touch); + break; + case RequestType::HexEdit: + data_component = CreateHexEdit(serializer, restore_func, touch); + break; + case RequestType::ComboBox: + data_component = CreateCombobox(serializer, restore_func, touch); + break; + default: + UNIMPLEMENTED(); + } + } else if (type == typeid(std::string)) { + switch (request) { + case RequestType::Default: + case RequestType::LineEdit: + data_component = CreateLineEdit(serializer, restore_func, touch); + break; + case RequestType::ComboBox: + data_component = CreateCombobox(serializer, restore_func, touch); + break; + default: + UNIMPLEMENTED(); + } + } + + if (data_component == nullptr) { + LOG_ERROR(Frontend, "Failed to create widget for \"{}\"", setting.GetLabel()); + created = false; + return; + } + + layout->addWidget(data_component); + + if (!managed) { + return; + } + + if (Settings::IsConfiguringGlobal()) { + load_func = [this, serializer, checkbox_serializer, require_checkbox, other_setting]() { + if (require_checkbox) { + other_setting->LoadString(checkbox_serializer()); + } + setting.LoadString(serializer()); + }; + } else { + layout->addWidget(restore_button); + + QObject::connect(restore_button, &QAbstractButton::clicked, + [this, restore_func, checkbox_restore_func](bool) { + LOG_DEBUG(Frontend, "Restore global state for \"{}\"", + setting.GetLabel()); + + restore_button->setEnabled(false); + restore_button->setVisible(false); + + checkbox_restore_func(); + restore_func(); + }); + + load_func = [this, serializer, require_checkbox, checkbox_serializer, other_setting]() { + bool using_global = !restore_button->isEnabled(); + setting.SetGlobal(using_global); + if (!using_global) { + setting.LoadString(serializer()); + } + if (require_checkbox) { + other_setting->SetGlobal(using_global); + if (!using_global) { + other_setting->LoadString(checkbox_serializer()); + } + } + }; + } + + if (other_setting != nullptr) { + const auto reset = [restore_func, data_component](int state) { + data_component->setEnabled(state == Qt::Checked); + if (state != Qt::Checked) { + restore_func(); + } + }; + connect(checkbox, &QCheckBox::stateChanged, reset); + reset(checkbox->checkState()); + } +} + +bool Widget::Valid() const { + return created; +} + +Widget::~Widget() = default; + +Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translations_, + const ComboboxTranslationMap& combobox_translations_, QWidget* parent_, + bool runtime_lock_, std::vector<std::function<void(bool)>>& apply_funcs_, + RequestType request, bool managed, float multiplier, + Settings::BasicSetting* other_setting, const QString& suffix) + : QWidget(parent_), parent{parent_}, translations{translations_}, + combobox_enumerations{combobox_translations_}, setting{*setting_}, apply_funcs{apply_funcs_}, + runtime_lock{runtime_lock_} { + if (!Settings::IsConfiguringGlobal() && !setting.Switchable()) { + LOG_DEBUG(Frontend, "\"{}\" is not switchable, skipping...", setting.GetLabel()); + return; + } + + const int id = setting.Id(); + + const auto [label, tooltip] = [&]() { + const auto& setting_label = setting.GetLabel(); + if (translations.contains(id)) { + return std::pair{translations.at(id).first, translations.at(id).second}; + } + LOG_WARNING(Frontend, "Translation table lacks entry for \"{}\"", setting_label); + return std::pair{QString::fromStdString(setting_label), QStringLiteral("")}; + }(); + + if (label == QStringLiteral("")) { + LOG_DEBUG(Frontend, "Translation table has empty entry for \"{}\", skipping...", + setting.GetLabel()); + return; + } + + std::function<void()> load_func = []() {}; + + SetupComponent(label, load_func, managed, request, multiplier, other_setting, suffix); + + if (!created) { + LOG_WARNING(Frontend, "No widget was created for \"{}\"", setting.GetLabel()); + return; + } + + apply_funcs.push_back([load_func, setting_](bool powered_on) { + if (setting_->RuntimeModfiable() || !powered_on) { + load_func(); + } + }); + + bool enable = runtime_lock || setting.RuntimeModfiable(); + if (setting.Switchable() && Settings::IsConfiguringGlobal() && !runtime_lock) { + enable &= setting.UsingGlobal(); + } + this->setEnabled(enable); + + this->setToolTip(tooltip); +} + +Builder::Builder(QWidget* parent_, bool runtime_lock_) + : translations{InitializeTranslations(parent_)}, + combobox_translations{ComboboxEnumeration(parent_)}, parent{parent_}, runtime_lock{ + runtime_lock_} {} + +Builder::~Builder() = default; + +Widget* Builder::BuildWidget(Settings::BasicSetting* setting, + std::vector<std::function<void(bool)>>& apply_funcs, + RequestType request, bool managed, float multiplier, + Settings::BasicSetting* other_setting, const QString& suffix) const { + if (!Settings::IsConfiguringGlobal() && !setting->Switchable()) { + return nullptr; + } + + if (setting->Specialization() == Settings::Specialization::Paired) { + LOG_DEBUG(Frontend, "\"{}\" has specialization Paired: ignoring", setting->GetLabel()); + return nullptr; + } + + return new Widget(setting, *translations, *combobox_translations, parent, runtime_lock, + apply_funcs, request, managed, multiplier, other_setting, suffix); +} + +Widget* Builder::BuildWidget(Settings::BasicSetting* setting, + std::vector<std::function<void(bool)>>& apply_funcs, + Settings::BasicSetting* other_setting, RequestType request, + const QString& suffix) const { + return BuildWidget(setting, apply_funcs, request, true, 1.0f, other_setting, suffix); +} + +const ComboboxTranslationMap& Builder::ComboboxTranslations() const { + return *combobox_translations; +} + +} // namespace ConfigurationShared diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h new file mode 100644 index 0000000000..e64693bab3 --- /dev/null +++ b/src/yuzu/configuration/shared_widget.h @@ -0,0 +1,161 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <functional> +#include <memory> +#include <string> +#include <vector> +#include <QString> +#include <QStringLiteral> +#include <QWidget> +#include <qobjectdefs.h> +#include "yuzu/configuration/shared_translation.h" + +class QCheckBox; +class QComboBox; +class QDateTimeEdit; +class QLabel; +class QLineEdit; +class QObject; +class QPushButton; +class QSlider; +class QSpinBox; + +namespace Settings { +class BasicSetting; +} // namespace Settings + +namespace ConfigurationShared { + +enum class RequestType { + Default, + ComboBox, + SpinBox, + Slider, + ReverseSlider, + LineEdit, + HexEdit, + DateTimeEdit, + MaxEnum, +}; + +class Widget : public QWidget { + Q_OBJECT + +public: + /** + * @param setting The primary Setting to create the Widget for + * @param translations Map of translations to display on the left side label/checkbox + * @param combobox_translations Map of translations for enumerating combo boxes + * @param parent Qt parent + * @param runtime_lock Emulated guest powered on state, for use on settings that should be + * configured during guest execution + * @param apply_funcs_ List to append, functions to run to apply the widget state to the setting + * @param request What type of data representation component to create -- not always respected + * for the Setting data type + * @param managed Set true if the caller will set up component data and handling + * @param multiplier Value to multiply the slider feedback label + * @param other_setting Second setting to modify, to replace the label with a checkbox + * @param suffix Set to specify formats for Slider feedback labels or SpinBox + */ + explicit Widget(Settings::BasicSetting* setting, const TranslationMap& translations, + const ComboboxTranslationMap& combobox_translations, QWidget* parent, + bool runtime_lock, std::vector<std::function<void(bool)>>& apply_funcs_, + RequestType request = RequestType::Default, bool managed = true, + float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr, + const QString& suffix = QStringLiteral("")); + virtual ~Widget(); + + /** + * @returns True if the Widget successfully created the components for the setting + */ + bool Valid() const; + + /** + * Creates a button to appear when a setting has been modified. This exists for custom + * configurations and wasn't designed to work for the global configuration. It has public access + * for settings that need to be unmanaged but can be custom. + * + * @param using_global The global state of the setting this button is for + * @param parent QWidget parent + */ + [[nodiscard]] static QPushButton* CreateRestoreGlobalButton(bool using_global, QWidget* parent); + + // Direct handles to sub components created + QPushButton* restore_button{}; ///< Restore button for custom configurations + QLineEdit* line_edit{}; ///< QLineEdit, used for LineEdit and HexEdit + QSpinBox* spinbox{}; + QCheckBox* checkbox{}; + QSlider* slider{}; + QComboBox* combobox{}; + QDateTimeEdit* date_time_edit{}; + +private: + void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed, + RequestType request, float multiplier, + Settings::BasicSetting* other_setting, const QString& suffix); + + QLabel* CreateLabel(const QString& text); + QWidget* CreateCheckBox(Settings::BasicSetting* bool_setting, const QString& label, + std::function<std::string()>& serializer, + std::function<void()>& restore_func, + const std::function<void()>& touch); + + QWidget* CreateCombobox(std::function<std::string()>& serializer, + std::function<void()>& restore_func, + const std::function<void()>& touch); + QWidget* CreateLineEdit(std::function<std::string()>& serializer, + std::function<void()>& restore_func, const std::function<void()>& touch, + bool managed = true); + QWidget* CreateHexEdit(std::function<std::string()>& serializer, + std::function<void()>& restore_func, const std::function<void()>& touch); + QWidget* CreateSlider(bool reversed, float multiplier, const QString& suffix, + std::function<std::string()>& serializer, + std::function<void()>& restore_func, const std::function<void()>& touch); + QWidget* CreateDateTimeEdit(bool disabled, bool restrict, + std::function<std::string()>& serializer, + std::function<void()>& restore_func, + const std::function<void()>& touch); + QWidget* CreateSpinBox(const QString& suffix, std::function<std::string()>& serializer, + std::function<void()>& restore_func, const std::function<void()>& touch); + + QWidget* parent; + const TranslationMap& translations; + const ComboboxTranslationMap& combobox_enumerations; + Settings::BasicSetting& setting; + std::vector<std::function<void(bool)>>& apply_funcs; + + bool created{false}; + bool runtime_lock{false}; +}; + +class Builder { +public: + explicit Builder(QWidget* parent, bool runtime_lock); + ~Builder(); + + Widget* BuildWidget(Settings::BasicSetting* setting, + std::vector<std::function<void(bool)>>& apply_funcs, + RequestType request = RequestType::Default, bool managed = true, + float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr, + const QString& suffix = QStringLiteral("")) const; + + Widget* BuildWidget(Settings::BasicSetting* setting, + std::vector<std::function<void(bool)>>& apply_funcs, + Settings::BasicSetting* other_setting, + RequestType request = RequestType::Default, + const QString& suffix = QStringLiteral("")) const; + + const ComboboxTranslationMap& ComboboxTranslations() const; + +private: + std::unique_ptr<TranslationMap> translations; + std::unique_ptr<ComboboxTranslationMap> combobox_translations; + + QWidget* parent; + const bool runtime_lock; +}; + +} // namespace ConfigurationShared diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 6cd557c294..97ae9e49a2 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -24,6 +24,7 @@ #include "applets/qt_software_keyboard.h" #include "applets/qt_web_browser.h" #include "common/nvidia_flags.h" +#include "common/settings_enums.h" #include "configuration/configure_input.h" #include "configuration/configure_per_game.h" #include "configuration/configure_tas.h" @@ -1095,10 +1096,9 @@ void GMainWindow::InitializeWidgets() { aa_status_button->setFocusPolicy(Qt::NoFocus); connect(aa_status_button, &QPushButton::clicked, [&] { auto aa_mode = Settings::values.anti_aliasing.GetValue(); - if (aa_mode == Settings::AntiAliasing::LastAA) { + aa_mode = static_cast<Settings::AntiAliasing>(static_cast<u32>(aa_mode) + 1); + if (aa_mode == Settings::AntiAliasing::MaxEnum) { aa_mode = Settings::AntiAliasing::None; - } else { - aa_mode = static_cast<Settings::AntiAliasing>(static_cast<u32>(aa_mode) + 1); } Settings::values.anti_aliasing.SetValue(aa_mode); aa_status_button->setChecked(true); @@ -1183,7 +1183,7 @@ void GMainWindow::InitializeWidgets() { QMenu context_menu; for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) { - if (gpu_accuracy_pair.first == Settings::GPUAccuracy::Extreme) { + if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) { continue; } context_menu.addAction(gpu_accuracy_pair.second, [this, gpu_accuracy_pair] { @@ -3651,14 +3651,14 @@ void GMainWindow::OnToggleDockedMode() { void GMainWindow::OnToggleGpuAccuracy() { switch (Settings::values.gpu_accuracy.GetValue()) { - case Settings::GPUAccuracy::High: { - Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::Normal); + case Settings::GpuAccuracy::High: { + Settings::values.gpu_accuracy.SetValue(Settings::GpuAccuracy::Normal); break; } - case Settings::GPUAccuracy::Normal: - case Settings::GPUAccuracy::Extreme: + case Settings::GpuAccuracy::Normal: + case Settings::GpuAccuracy::Extreme: default: { - Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); + Settings::values.gpu_accuracy.SetValue(Settings::GpuAccuracy::High); break; } } @@ -3702,10 +3702,9 @@ void GMainWindow::OnIncreaseVolume() { void GMainWindow::OnToggleAdaptingFilter() { auto filter = Settings::values.scaling_filter.GetValue(); - if (filter == Settings::ScalingFilter::LastFilter) { + filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1); + if (filter == Settings::ScalingFilter::MaxEnum) { filter = Settings::ScalingFilter::NearestNeighbor; - } else { - filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1); } Settings::values.scaling_filter.SetValue(filter); filter_status_button->setChecked(true); @@ -4071,7 +4070,7 @@ void GMainWindow::UpdateGPUAccuracyButton() { const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second; gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); - gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GPUAccuracy::Normal); + gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal); } void GMainWindow::UpdateDockedButton() { diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp index d71cc23a72..a415a953f6 100644 --- a/src/yuzu/multiplayer/direct_connect.cpp +++ b/src/yuzu/multiplayer/direct_connect.cpp @@ -34,13 +34,14 @@ DirectConnectWindow::DirectConnectWindow(Core::System& system_, QWidget* parent) connect(watcher, &QFutureWatcher<void>::finished, this, &DirectConnectWindow::OnConnection); ui->nickname->setValidator(validation.GetNickname()); - ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); + ui->nickname->setText( + QString::fromStdString(UISettings::values.multiplayer_nickname.GetValue())); if (ui->nickname->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { // Use yuzu Web Service user name as nickname by default ui->nickname->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); } ui->ip->setValidator(validation.GetIP()); - ui->ip->setText(UISettings::values.multiplayer_ip.GetValue()); + ui->ip->setText(QString::fromStdString(UISettings::values.multiplayer_ip.GetValue())); ui->port->setValidator(validation.GetPort()); ui->port->setText(QString::number(UISettings::values.multiplayer_port.GetValue())); @@ -91,8 +92,8 @@ void DirectConnectWindow::Connect() { } // Store settings - UISettings::values.multiplayer_nickname = ui->nickname->text(); - UISettings::values.multiplayer_ip = ui->ip->text(); + UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString(); + UISettings::values.multiplayer_ip = ui->ip->text().toStdString(); if (ui->port->isModified() && !ui->port->text().isEmpty()) { UISettings::values.multiplayer_port = ui->port->text().toInt(); } else { diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp index a8faa5b248..ef364ee43a 100644 --- a/src/yuzu/multiplayer/host_room.cpp +++ b/src/yuzu/multiplayer/host_room.cpp @@ -55,12 +55,14 @@ HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, connect(ui->host, &QPushButton::clicked, this, &HostRoomWindow::Host); // Restore the settings: - ui->username->setText(UISettings::values.multiplayer_room_nickname.GetValue()); + ui->username->setText( + QString::fromStdString(UISettings::values.multiplayer_room_nickname.GetValue())); if (ui->username->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { // Use yuzu Web Service user name as nickname by default ui->username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); } - ui->room_name->setText(UISettings::values.multiplayer_room_name.GetValue()); + ui->room_name->setText( + QString::fromStdString(UISettings::values.multiplayer_room_name.GetValue())); ui->port->setText(QString::number(UISettings::values.multiplayer_room_port.GetValue())); ui->max_player->setValue(UISettings::values.multiplayer_max_player.GetValue()); int index = UISettings::values.multiplayer_host_type.GetValue(); @@ -72,7 +74,8 @@ HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, if (index != -1) { ui->game_list->setCurrentIndex(index); } - ui->room_description->setText(UISettings::values.multiplayer_room_description.GetValue()); + ui->room_description->setText( + QString::fromStdString(UISettings::values.multiplayer_room_description.GetValue())); } HostRoomWindow::~HostRoomWindow() = default; @@ -218,8 +221,8 @@ void HostRoomWindow::Host() { Network::NoPreferredIP, password, token); // Store settings - UISettings::values.multiplayer_room_nickname = ui->username->text(); - UISettings::values.multiplayer_room_name = ui->room_name->text(); + UISettings::values.multiplayer_room_nickname = ui->username->text().toStdString(); + UISettings::values.multiplayer_room_name = ui->room_name->text().toStdString(); UISettings::values.multiplayer_game_id = ui->game_list->currentData(GameListItemPath::ProgramIdRole).toLongLong(); UISettings::values.multiplayer_max_player = ui->max_player->value(); @@ -230,7 +233,8 @@ void HostRoomWindow::Host() { } else { UISettings::values.multiplayer_room_port = Network::DefaultRoomPort; } - UISettings::values.multiplayer_room_description = ui->room_description->toPlainText(); + UISettings::values.multiplayer_room_description = + ui->room_description->toPlainText().toStdString(); ui->host->setEnabled(true); emit SaveConfig(); close(); diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp index 387f6f7c91..603e9ae3de 100644 --- a/src/yuzu/multiplayer/lobby.cpp +++ b/src/yuzu/multiplayer/lobby.cpp @@ -60,7 +60,8 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, ui->room_list->setContextMenuPolicy(Qt::CustomContextMenu); ui->nickname->setValidator(validation.GetNickname()); - ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); + ui->nickname->setText( + QString::fromStdString(UISettings::values.multiplayer_nickname.GetValue())); // Try find the best nickname by default if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("yuzu")) { @@ -202,9 +203,9 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { // TODO(jroweboy): disable widgets and display a connecting while we wait // Save settings - UISettings::values.multiplayer_nickname = ui->nickname->text(); + UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString(); UISettings::values.multiplayer_ip = - proxy->data(connection_index, LobbyItemHost::HostIPRole).toString(); + proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString(); UISettings::values.multiplayer_port = proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt(); emit SaveConfig(); diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp index 2c1b547fba..f03dc01ddc 100644 --- a/src/yuzu/uisettings.cpp +++ b/src/yuzu/uisettings.cpp @@ -3,6 +3,18 @@ #include "yuzu/uisettings.h" +#ifndef CANNOT_EXPLICITLY_INSTANTIATE +namespace Settings { +template class Setting<bool>; +template class Setting<std::string>; +template class Setting<u16, true>; +template class Setting<u32>; +template class Setting<u8, true>; +template class Setting<u8>; +template class Setting<unsigned long long>; +} // namespace Settings +#endif + namespace UISettings { const Themes themes{{ diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 20a517d34a..ee8c9f2148 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -14,6 +14,21 @@ #include "common/common_types.h" #include "common/settings.h" +using Settings::Category; +using Settings::Setting; + +#ifndef CANNOT_EXPLICITLY_INSTANTIATE +namespace Settings { +extern template class Setting<bool>; +extern template class Setting<std::string>; +extern template class Setting<u16, true>; +extern template class Setting<u32>; +extern template class Setting<u8, true>; +extern template class Setting<u8>; +extern template class Setting<unsigned long long>; +} // namespace Settings +#endif + namespace UISettings { bool IsDarkTheme(); @@ -56,6 +71,8 @@ struct GameDir { }; struct Values { + Settings::Linkage linkage{1000}; + QByteArray geometry; QByteArray state; @@ -64,30 +81,37 @@ struct Values { QByteArray gamelist_header_state; QByteArray microprofile_geometry; - Settings::Setting<bool> microprofile_visible{false, "microProfileDialogVisible"}; - - 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"}; - - 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> mute_when_in_background{false, "muteWhenInBackground"}; - Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"}; - Settings::Setting<bool> controller_applet_disabled{false, "disableControllerApplet"}; - + Setting<bool> microprofile_visible{linkage, false, "microProfileDialogVisible", + Category::UiLayout}; + + Setting<bool> single_window_mode{linkage, true, "singleWindowMode", Category::Ui}; + Setting<bool> fullscreen{linkage, false, "fullscreen", Category::Ui}; + Setting<bool> display_titlebar{linkage, true, "displayTitleBars", Category::Ui}; + Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui}; + Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui}; + + Setting<bool> confirm_before_closing{linkage, true, "confirmClose", Category::UiGeneral}; + Setting<bool> first_start{linkage, true, "firstStart", Category::Ui}; + Setting<bool> pause_when_in_background{linkage, false, "pauseWhenInBackground", + Category::UiGeneral}; + Setting<bool> mute_when_in_background{linkage, false, "muteWhenInBackground", Category::Ui}; + Setting<bool> hide_mouse{linkage, true, "hideInactiveMouse", Category::UiGeneral}; + Setting<bool> controller_applet_disabled{linkage, false, "disableControllerApplet", + Category::UiGeneral}; // Set when Vulkan is known to crash the application bool has_broken_vulkan = false; - Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"}; + Setting<bool> select_user_on_boot{linkage, false, "select_user_on_boot", Category::UiGeneral}; + Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Ui}; // Discord RPC - Settings::Setting<bool> enable_discord_presence{true, "enable_discord_presence"}; + Setting<bool> enable_discord_presence{linkage, true, "enable_discord_presence", Category::Ui}; - Settings::Setting<bool> enable_screenshot_save_as{true, "enable_screenshot_save_as"}; + // logging + Setting<bool> show_console{linkage, false, "showConsole", Category::Ui}; + + Setting<bool> enable_screenshot_save_as{linkage, true, "enable_screenshot_save_as", + Category::Screenshots}; QString roms_path; QString symbols_path; @@ -102,47 +126,46 @@ struct Values { // Shortcut name <Shortcut, context> std::vector<Shortcut> shortcuts; - Settings::Setting<uint32_t> callout_flags{0, "calloutFlags"}; + Setting<u32> callout_flags{linkage, 0, "calloutFlags", Category::Ui}; // multiplayer settings - Settings::Setting<QString> multiplayer_nickname{{}, "nickname"}; - Settings::Setting<QString> multiplayer_ip{{}, "ip"}; - Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, UINT16_MAX, "port"}; - Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"}; - Settings::Setting<QString> multiplayer_room_name{{}, "room_name"}; - Settings::SwitchableSetting<uint, true> multiplayer_max_player{8, 0, 8, "max_player"}; - Settings::SwitchableSetting<uint, true> multiplayer_room_port{24872, 0, UINT16_MAX, - "room_port"}; - Settings::SwitchableSetting<uint, true> multiplayer_host_type{0, 0, 1, "host_type"}; - Settings::Setting<qulonglong> multiplayer_game_id{{}, "game_id"}; - Settings::Setting<QString> multiplayer_room_description{{}, "room_description"}; + Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer}; + Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer}; + Setting<u16, true> multiplayer_port{linkage, 24872, 0, + UINT16_MAX, "port", Category::Multiplayer}; + Setting<std::string> multiplayer_room_nickname{ + linkage, {}, "room_nickname", Category::Multiplayer}; + Setting<std::string> multiplayer_room_name{linkage, {}, "room_name", Category::Multiplayer}; + Setting<u8, true> multiplayer_max_player{linkage, 8, 0, 8, "max_player", Category::Multiplayer}; + Setting<u16, true> multiplayer_room_port{linkage, 24872, 0, + UINT16_MAX, "room_port", Category::Multiplayer}; + Setting<u8, true> multiplayer_host_type{linkage, 0, 0, 1, "host_type", Category::Multiplayer}; + Setting<unsigned long long> multiplayer_game_id{linkage, {}, "game_id", Category::Multiplayer}; + Setting<std::string> multiplayer_room_description{ + linkage, {}, "room_description", Category::Multiplayer}; std::pair<std::vector<std::string>, std::vector<std::string>> multiplayer_ban_list; - // logging - Settings::Setting<bool> show_console{false, "showConsole"}; - // Game List - Settings::Setting<bool> show_add_ons{true, "show_add_ons"}; - Settings::Setting<uint32_t> game_icon_size{64, "game_icon_size"}; - Settings::Setting<uint32_t> folder_icon_size{48, "folder_icon_size"}; - Settings::Setting<uint8_t> row_1_text_id{3, "row_1_text_id"}; - Settings::Setting<uint8_t> row_2_text_id{2, "row_2_text_id"}; + Setting<bool> show_add_ons{linkage, true, "show_add_ons", Category::UiGameList}; + Setting<u32> game_icon_size{linkage, 64, "game_icon_size", Category::UiGameList}; + Setting<u32> folder_icon_size{linkage, 48, "folder_icon_size", Category::UiGameList}; + Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList}; + Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList}; std::atomic_bool is_game_list_reload_pending{false}; - Settings::Setting<bool> cache_game_list{true, "cache_game_list"}; - Settings::Setting<bool> favorites_expanded{true, "favorites_expanded"}; + Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList}; + Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList}; QVector<u64> favorited_ids; // Compatibility List - Settings::Setting<bool> show_compat{false, "show_compat"}; + Setting<bool> show_compat{linkage, false, "show_compat", Category::UiGameList}; // Size & File Types Column - Settings::Setting<bool> show_size{true, "show_size"}; - Settings::Setting<bool> show_types{true, "show_types"}; + Setting<bool> show_size{linkage, true, "show_size", Category::UiGameList}; + Setting<bool> show_types{linkage, true, "show_types", Category::UiGameList}; bool configuration_applied; bool reset_to_defaults; bool shortcut_already_warned{false}; - Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; }; extern Values values; |