code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "yuzu/configuration/shared_translation.h"

#include <map>
#include <memory>
#include <tuple>
#include <utility>
#include <QCoreApplication>
#include <QWidget>
#include "common/settings.h"
#include "common/settings_enums.h"
#include "common/settings_setting.h"
#include "common/time_zone.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{(NAME), (TOOLTIP)}})

    // A setting can be ignored by giving it a blank name

    // Applets
    INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QStringLiteral());
    INSERT(Settings, controller_applet_mode, tr("Controller configuration"), QStringLiteral());
    INSERT(Settings, data_erase_applet_mode, tr("Data erase"), QStringLiteral());
    INSERT(Settings, error_applet_mode, tr("Error"), QStringLiteral());
    INSERT(Settings, net_connect_applet_mode, tr("Net connect"), QStringLiteral());
    INSERT(Settings, player_select_applet_mode, tr("Player select"), QStringLiteral());
    INSERT(Settings, swkbd_applet_mode, tr("Software keyboard"), QStringLiteral());
    INSERT(Settings, mii_edit_applet_mode, tr("Mii Edit"), QStringLiteral());
    INSERT(Settings, web_applet_mode, tr("Online web"), QStringLiteral());
    INSERT(Settings, shop_applet_mode, tr("Shop"), QStringLiteral());
    INSERT(Settings, photo_viewer_applet_mode, tr("Photo viewer"), QStringLiteral());
    INSERT(Settings, offline_web_applet_mode, tr("Offline web"), QStringLiteral());
    INSERT(Settings, login_share_applet_mode, tr("Login share"), QStringLiteral());
    INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QStringLiteral());
    INSERT(Settings, my_page_applet_mode, tr("My page"), QStringLiteral());

    // Audio
    INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral());
    INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral());
    INSERT(Settings, audio_input_device_id, tr("Input Device:"), QStringLiteral());
    INSERT(Settings, audio_muted, tr("Mute audio"), QStringLiteral());
    INSERT(Settings, volume, tr("Volume:"), QStringLiteral());
    INSERT(Settings, dump_audio_commands, QStringLiteral(), QStringLiteral());
    INSERT(UISettings, mute_when_in_background, tr("Mute audio when in background"),
           QStringLiteral());

    // Core
    INSERT(
        Settings, use_multi_core, tr("Multicore CPU Emulation"),
        tr("This option increases CPU emulation thread use from 1 to the Switch’s maximum of 4.\n"
           "This is mainly a debug option and shouldn’t be disabled."));
    INSERT(
        Settings, memory_layout_mode, tr("Memory Layout"),
        tr("Increases the amount of emulated RAM from the stock 4GB of the retail Switch to the "
           "developer kit's 8/6GB.\nIt’s doesn’t improve stability or performance and is intended "
           "to let big texture mods fit in emulated RAM.\nEnabling it will increase memory "
           "use. It is not recommended to enable unless a specific game with a texture mod needs "
           "it."));
    INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral());
    INSERT(Settings, speed_limit, tr("Limit Speed Percent"),
           tr("Controls the game's maximum rendering speed, but it’s up to each game if it runs "
              "faster or not.\n200% for a 30 FPS game is 60 FPS, and for a "
              "60 FPS game it will be 120 FPS.\nDisabling it means unlocking the framerate to the "
              "maximum your PC can reach."));

    // Cpu
    INSERT(Settings, cpu_accuracy, tr("Accuracy:"),
           tr("This setting controls the accuracy of the emulated CPU.\nDon't change this unless "
              "you know what you are doing."));
    INSERT(Settings, cpu_backend, tr("Backend:"), QStringLiteral());

    // Cpu Debug

    // Cpu Unsafe
    INSERT(
        Settings, cpuopt_unsafe_unfuse_fma,
        tr("Unfuse FMA (improve performance on CPUs without FMA)"),
        tr("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, tr("Faster FRSQRTE and FRECPE"),
        tr("This option improves the speed of some approximate floating-point functions by using "
           "less accurate native approximations."));
    INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr,
           tr("Faster ASIMD instructions (32 bits only)"),
           tr("This option improves the speed of 32 bits ASIMD floating-point functions by running "
              "with incorrect rounding modes."));
    INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"),
           tr("This option improves speed by removing NaN checking.\nPlease note this also reduces "
              "accuracy of certain floating-point instructions."));
    INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"),
           tr("This option improves speed by eliminating a safety check before every memory "
              "read/write in guest.\nDisabling it may allow a game to read/write the emulator's "
              "memory."));
    INSERT(
        Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"),
        tr("This option improves speed by relying only on the semantics of cmpxchg to ensure "
           "safety of exclusive access instructions.\nPlease note this may result in deadlocks and "
           "other race conditions."));

    // Renderer
    INSERT(
        Settings, renderer_backend, tr("API:"),
        tr("Switches between the available graphics APIs.\nVulkan is recommended in most cases."));
    INSERT(Settings, vulkan_device, tr("Device:"),
           tr("This setting selects the GPU to use with the Vulkan backend."));
    INSERT(Settings, shader_backend, tr("Shader Backend:"),
           tr("The shader backend to use for the OpenGL renderer.\nGLSL is the fastest in "
              "performance and the best in rendering accuracy.\n"
              "GLASM is a deprecated NVIDIA-only backend that offers much better shader building "
              "performance at the cost of FPS and rendering accuracy.\n"
              "SPIR-V compiles the fastest, but yields poor results on most GPU drivers."));
    INSERT(Settings, resolution_setup, tr("Resolution:"),
           tr("Forces the game to render at a different resolution.\nHigher resolutions require "
              "much more VRAM and bandwidth.\n"
              "Options lower than 1X can cause rendering issues."));
    INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral());
    INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"),
           tr("Determines how sharpened the image will look while using FSR’s dynamic contrast."));
    INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"),
           tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA has a "
              "lower performance impact and can produce a better and more stable picture under "
              "very low resolutions."));
    INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"),
           tr("The method used to render the window in fullscreen.\nBorderless offers the best "
              "compatibility with the on-screen keyboard that some games request for "
              "input.\nExclusive "
              "fullscreen may offer better performance and better Freesync/Gsync support."));
    INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"),
           tr("Stretches the game to fit the specified aspect ratio.\nSwitch games only support "
              "16:9, so custom game mods are required to get other ratios.\nAlso controls the "
              "aspect ratio of captured screenshots."));
    INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"),
           tr("Allows saving shaders to storage for faster loading on following game "
              "boots.\nDisabling "
              "it is only intended for debugging."));
    INSERT(
        Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
        tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
    INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"),
           tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for "
              "decoding, or perform no decoding at all (black screen on videos).\n"
              "In most cases, GPU decoding provides the best performance."));
    INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"),
           tr("This option controls how ASTC textures should be decoded.\n"
              "CPU: Use the CPU for decoding, slowest but safest method.\n"
              "GPU: Use the GPU's compute shaders to decode ASTC textures, recommended for most "
              "games and users.\n"
              "CPU Asynchronously: Use the CPU to decode ASTC textures as they arrive. Completely "
              "eliminates ASTC decoding\nstuttering at the cost of rendering issues while the "
              "texture is being decoded."));
    INSERT(
        Settings, astc_recompression, tr("ASTC Recompression Method:"),
        tr("Almost all desktop and laptop dedicated GPUs lack support for ASTC textures, forcing "
           "the emulator to decompress to an intermediate format any card supports, RGBA8.\n"
           "This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but "
           "negatively affecting image quality."));
    INSERT(Settings, vram_usage_mode, tr("VRAM Usage Mode:"),
           tr("Selects whether the emulator should prefer to conserve memory or make maximum usage "
              "of available video memory for performance. Has no effect on integrated graphics. "
              "Aggressive mode may severely impact the performance of other applications such as "
              "recording software."));
    INSERT(
        Settings, vsync_mode, tr("VSync Mode:"),
        tr("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, QStringLiteral(), QStringLiteral());
    INSERT(Settings, bg_green, QStringLiteral(), QStringLiteral());
    INSERT(Settings, bg_blue, QStringLiteral(), QStringLiteral());

    // Renderer (Advanced Graphics)
    INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"),
           tr("Slightly improves performance by moving presentation to a separate CPU thread."));
    INSERT(
        Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"),
        tr("Runs work in the background while waiting for graphics commands to keep the GPU from "
           "lowering its clock speed."));
    INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"),
           tr("Controls the quality of texture rendering at oblique angles.\nIt’s a light setting "
              "and safe to set at 16x on most GPUs."));
    INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"),
           tr("GPU emulation accuracy.\nMost games render fine with Normal, but High is still "
              "required for some.\nParticles tend to only render correctly with High "
              "accuracy.\nExtreme should only be used for debugging.\nThis option can "
              "be changed while playing.\nSome games may require booting on high to render "
              "properly."));
    INSERT(Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"),
           tr("Enables asynchronous shader compilation, which may reduce shader stutter.\nThis "
              "feature "
              "is experimental."));
    INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"),
           tr("Enables Fast GPU Time. This option will force most games to run at their highest "
              "native resolution."));
    INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"),
           tr("Enables GPU vendor-specific pipeline cache.\nThis 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, tr("Enable Compute Pipelines (Intel Vulkan Only)"),
        tr("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, tr("Enable Reactive Flushing"),
        tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory "
           "syncing."));
    INSERT(Settings, use_video_framerate, tr("Sync to framerate of video playback"),
           tr("Run the game at normal speed during video playback, even when the framerate is "
              "unlocked."));
    INSERT(Settings, barrier_feedback_loops, tr("Barrier feedback loops"),
           tr("Improves rendering of transparency effects in specific games."));

    // Renderer (Debug)

    // System
    INSERT(Settings, rng_seed, tr("RNG Seed"),
           tr("Controls the seed of the random number generator.\nMainly used for speedrunning "
              "purposes."));
    INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral());
    INSERT(Settings, device_name, tr("Device Name"), tr("The name of the emulated Switch."));
    INSERT(Settings, custom_rtc, tr("Custom RTC Date:"),
           tr("This option allows to change the emulated clock of the Switch.\n"
              "Can be used to manipulate time in games."));
    INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral());
    INSERT(Settings, custom_rtc_offset, QStringLiteral(" "),
           QStringLiteral("The number of seconds from the current unix time"));
    INSERT(Settings, language_index, tr("Language:"),
           tr("Note: this can be overridden when region setting is auto-select"));
    INSERT(Settings, region_index, tr("Region:"), tr("The region of the emulated Switch."));
    INSERT(Settings, time_zone_index, tr("Time Zone:"),
           tr("The time zone of the emulated Switch."));
    INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral());
    INSERT(Settings, use_docked_mode, tr("Console Mode:"),
           tr("Selects if the console is emulated in Docked or Handheld mode.\nGames will change "
              "their resolution, details and supported controllers and depending on this setting.\n"
              "Setting to Handheld can help improve performance for low end systems."));
    INSERT(Settings, current_user, QStringLiteral(), QStringLiteral());

    // Controls

    // Data Storage

    // Debugging

    // Debugging Graphics

    // Network

    // Web Service

    // Ui

    // Ui General
    INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"),
           tr("Ask to select a user profile on each boot, useful if multiple people use yuzu on "
              "the same PC."));
    INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"),
           tr("This setting pauses yuzu when focusing other windows."));
    INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"),
           tr("This setting overrides game prompts asking to confirm stopping the game.\nEnabling "
              "it bypasses such prompts and directly exits the emulation."));
    INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"),
           tr("This setting hides the mouse after 2.5s of inactivity."));
    INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
           tr("Forcibly disables the use of the controller applet by guests.\nWhen a guest "
              "attempts to open the controller applet, it is immediately closed."));

    // Linux
    INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral());

    // 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), (TRANSLATION)}

    // Intentionally skipping VSyncMode to let the UI fill that one out
    translations->insert({Settings::EnumMetadata<Settings::AppletMode>::Index(),
                          {
                              PAIR(AppletMode, HLE, tr("Custom frontend")),
                              PAIR(AppletMode, LLE, tr("Real applet")),
                          }});

    translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
                          {
                              PAIR(AstcDecodeMode, Cpu, tr("CPU")),
                              PAIR(AstcDecodeMode, Gpu, tr("GPU")),
                              PAIR(AstcDecodeMode, CpuAsynchronous, tr("CPU Asynchronous")),
                          }});
    translations->insert(
        {Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
         {
             PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")),
             PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),
             PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),
         }});
    translations->insert({Settings::EnumMetadata<Settings::VramUsageMode>::Index(),
                          {
                              PAIR(VramUsageMode, Conservative, tr("Conservative")),
                              PAIR(VramUsageMode, Aggressive, tr("Aggressive")),
                          }});
    translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
                          {
#ifdef HAS_OPENGL
                              PAIR(RendererBackend, OpenGL, tr("OpenGL")),
#endif
                              PAIR(RendererBackend, Vulkan, tr("Vulkan")),
                              PAIR(RendererBackend, Null, tr("Null")),
                          }});
    translations->insert(
        {Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
         {
             PAIR(ShaderBackend, Glsl, tr("GLSL")),
             PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")),
             PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, AMD/Mesa Only)")),
         }});
    translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
                          {
                              PAIR(GpuAccuracy, Normal, tr("Normal")),
                              PAIR(GpuAccuracy, High, tr("High")),
                              PAIR(GpuAccuracy, Extreme, tr("Extreme")),
                          }});
    translations->insert(
        {Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
         {
             PAIR(CpuAccuracy, Auto, tr("Auto")),
             PAIR(CpuAccuracy, Accurate, tr("Accurate")),
             PAIR(CpuAccuracy, Unsafe, tr("Unsafe")),
             PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")),
         }});
    translations->insert({Settings::EnumMetadata<Settings::CpuBackend>::Index(),
                          {
                              PAIR(CpuBackend, Dynarmic, tr("Dynarmic")),
                              PAIR(CpuBackend, Nce, tr("NCE")),
                          }});
    translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(),
                          {
                              PAIR(FullscreenMode, Borderless, tr("Borderless Windowed")),
                              PAIR(FullscreenMode, Exclusive, tr("Exclusive Fullscreen")),
                          }});
    translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(),
                          {
                              PAIR(NvdecEmulation, Off, tr("No Video Output")),
                              PAIR(NvdecEmulation, Cpu, tr("CPU Video Decoding")),
                              PAIR(NvdecEmulation, Gpu, tr("GPU Video Decoding (Default)")),
                          }});
    translations->insert(
        {Settings::EnumMetadata<Settings::ResolutionSetup>::Index(),
         {
             PAIR(ResolutionSetup, Res1_2X, tr("0.5X (360p/540p) [EXPERIMENTAL]")),
             PAIR(ResolutionSetup, Res3_4X, tr("0.75X (540p/810p) [EXPERIMENTAL]")),
             PAIR(ResolutionSetup, Res1X, tr("1X (720p/1080p)")),
             PAIR(ResolutionSetup, Res3_2X, tr("1.5X (1080p/1620p) [EXPERIMENTAL]")),
             PAIR(ResolutionSetup, Res2X, tr("2X (1440p/2160p)")),
             PAIR(ResolutionSetup, Res3X, tr("3X (2160p/3240p)")),
             PAIR(ResolutionSetup, Res4X, tr("4X (2880p/4320p)")),
             PAIR(ResolutionSetup, Res5X, tr("5X (3600p/5400p)")),
             PAIR(ResolutionSetup, Res6X, tr("6X (4320p/6480p)")),
             PAIR(ResolutionSetup, Res7X, tr("7X (5040p/7560p)")),
             PAIR(ResolutionSetup, Res8X, tr("8X (5760p/8640p)")),
         }});
    translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(),
                          {
                              PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")),
                              PAIR(ScalingFilter, Bilinear, tr("Bilinear")),
                              PAIR(ScalingFilter, Bicubic, tr("Bicubic")),
                              PAIR(ScalingFilter, Gaussian, tr("Gaussian")),
                              PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")),
                              PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™ Super Resolution")),
                          }});
    translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
                          {
                              PAIR(AntiAliasing, None, tr("None")),
                              PAIR(AntiAliasing, Fxaa, tr("FXAA")),
                              PAIR(AntiAliasing, Smaa, tr("SMAA")),
                          }});
    translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(),
                          {
                              PAIR(AspectRatio, R16_9, tr("Default (16:9)")),
                              PAIR(AspectRatio, R4_3, tr("Force 4:3")),
                              PAIR(AspectRatio, R21_9, tr("Force 21:9")),
                              PAIR(AspectRatio, R16_10, tr("Force 16:10")),
                              PAIR(AspectRatio, Stretch, tr("Stretch to Window")),
                          }});
    translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(),
                          {
                              PAIR(AnisotropyMode, Automatic, tr("Automatic")),
                              PAIR(AnisotropyMode, Default, tr("Default")),
                              PAIR(AnisotropyMode, X2, tr("2x")),
                              PAIR(AnisotropyMode, X4, tr("4x")),
                              PAIR(AnisotropyMode, X8, tr("8x")),
                              PAIR(AnisotropyMode, X16, tr("16x")),
                          }});
    translations->insert(
        {Settings::EnumMetadata<Settings::Language>::Index(),
         {
             PAIR(Language, Japanese, tr("Japanese (日本語)")),
             PAIR(Language, EnglishAmerican, tr("American English")),
             PAIR(Language, French, tr("French (français)")),
             PAIR(Language, German, tr("German (Deutsch)")),
             PAIR(Language, Italian, tr("Italian (italiano)")),
             PAIR(Language, Spanish, tr("Spanish (español)")),
             PAIR(Language, Chinese, tr("Chinese")),
             PAIR(Language, Korean, tr("Korean (한국어)")),
             PAIR(Language, Dutch, tr("Dutch (Nederlands)")),
             PAIR(Language, Portuguese, tr("Portuguese (português)")),
             PAIR(Language, Russian, tr("Russian (Русский)")),
             PAIR(Language, Taiwanese, tr("Taiwanese")),
             PAIR(Language, EnglishBritish, tr("British English")),
             PAIR(Language, FrenchCanadian, tr("Canadian French")),
             PAIR(Language, SpanishLatin, tr("Latin American Spanish")),
             PAIR(Language, ChineseSimplified, tr("Simplified Chinese")),
             PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")),
             PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")),
         }});
    translations->insert({Settings::EnumMetadata<Settings::Region>::Index(),
                          {
                              PAIR(Region, Japan, tr("Japan")),
                              PAIR(Region, Usa, tr("USA")),
                              PAIR(Region, Europe, tr("Europe")),
                              PAIR(Region, Australia, tr("Australia")),
                              PAIR(Region, China, tr("China")),
                              PAIR(Region, Korea, tr("Korea")),
                              PAIR(Region, Taiwan, tr("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, tr("CET")),
             PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")),
             PAIR(TimeZone, Cuba, tr("Cuba")),
             PAIR(TimeZone, Eet, tr("EET")),
             PAIR(TimeZone, Egypt, tr("Egypt")),
             PAIR(TimeZone, Eire, tr("Eire")),
             PAIR(TimeZone, Est, tr("EST")),
             PAIR(TimeZone, Est5Edt, tr("EST5EDT")),
             PAIR(TimeZone, Gb, tr("GB")),
             PAIR(TimeZone, GbEire, tr("GB-Eire")),
             PAIR(TimeZone, Gmt, tr("GMT")),
             PAIR(TimeZone, GmtPlusZero, tr("GMT+0")),
             PAIR(TimeZone, GmtMinusZero, tr("GMT-0")),
             PAIR(TimeZone, GmtZero, tr("GMT0")),
             PAIR(TimeZone, Greenwich, tr("Greenwich")),
             PAIR(TimeZone, Hongkong, tr("Hongkong")),
             PAIR(TimeZone, Hst, tr("HST")),
             PAIR(TimeZone, Iceland, tr("Iceland")),
             PAIR(TimeZone, Iran, tr("Iran")),
             PAIR(TimeZone, Israel, tr("Israel")),
             PAIR(TimeZone, Jamaica, tr("Jamaica")),
             PAIR(TimeZone, Japan, tr("Japan")),
             PAIR(TimeZone, Kwajalein, tr("Kwajalein")),
             PAIR(TimeZone, Libya, tr("Libya")),
             PAIR(TimeZone, Met, tr("MET")),
             PAIR(TimeZone, Mst, tr("MST")),
             PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")),
             PAIR(TimeZone, Navajo, tr("Navajo")),
             PAIR(TimeZone, Nz, tr("NZ")),
             PAIR(TimeZone, NzChat, tr("NZ-CHAT")),
             PAIR(TimeZone, Poland, tr("Poland")),
             PAIR(TimeZone, Portugal, tr("Portugal")),
             PAIR(TimeZone, Prc, tr("PRC")),
             PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")),
             PAIR(TimeZone, Roc, tr("ROC")),
             PAIR(TimeZone, Rok, tr("ROK")),
             PAIR(TimeZone, Singapore, tr("Singapore")),
             PAIR(TimeZone, Turkey, tr("Turkey")),
             PAIR(TimeZone, Uct, tr("UCT")),
             PAIR(TimeZone, Universal, tr("Universal")),
             PAIR(TimeZone, Utc, tr("UTC")),
             PAIR(TimeZone, WSu, tr("W-SU")),
             PAIR(TimeZone, Wet, tr("WET")),
             PAIR(TimeZone, Zulu, tr("Zulu")),
         }});
    translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(),
                          {
                              PAIR(AudioMode, Mono, tr("Mono")),
                              PAIR(AudioMode, Stereo, tr("Stereo")),
                              PAIR(AudioMode, Surround, tr("Surround")),
                          }});
    translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(),
                          {
                              PAIR(MemoryLayout, Memory_4Gb, tr("4GB DRAM (Default)")),
                              PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM (Unsafe)")),
                              PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM (Unsafe)")),
                          }});
    translations->insert({Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
                          {
                              PAIR(ConsoleMode, Docked, tr("Docked")),
                              PAIR(ConsoleMode, Handheld, tr("Handheld")),
                          }});
    translations->insert(
        {Settings::EnumMetadata<Settings::ConfirmStop>::Index(),
         {
             PAIR(ConfirmStop, Ask_Always, tr("Always ask (Default)")),
             PAIR(ConfirmStop, Ask_Based_On_Game, tr("Only if game specifies not to stop")),
             PAIR(ConfirmStop, Ask_Never, tr("Never ask")),
         }});

#undef PAIR
#undef CTX_PAIR

    return translations;
}
} // namespace ConfigurationShared


code:
// 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"
#include "common/settings.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);

static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = {
    {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))},
    {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))},
    {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))},
};

static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map = {
    {Settings::ScalingFilter::NearestNeighbor,
     QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))},
    {Settings::ScalingFilter::Bilinear,
     QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))},
    {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
    {Settings::ScalingFilter::Gaussian,
     QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))},
    {Settings::ScalingFilter::ScaleForce,
     QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
    {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
};

static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {
    {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
    {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
};

static const std::map<Settings::GpuAccuracy, QString> 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"))},
};

static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = {
    {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))},
    {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))},
    {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
};

static const std::map<Settings::ShaderBackend, QString> 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"))},
};

} // namespace ConfigurationShared

repo_name: litucks/torzu

license: mit


code:
// 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 <QRadioButton>
#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 default_suffix;
}

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::strtoul(setting.ToString().c_str(), nullptr, 0);
    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::strtoul(RelevantDefault(setting).c_str(), nullptr, 0);
        combobox->setCurrentIndex(find_index(global_value));
    };

    if (!Settings::IsConfiguringGlobal()) {
        QObject::connect(combobox, QOverload<int>::of(&QComboBox::activated),
                         [touch]() { touch(); });
    }

    return combobox;
}

QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer,
                                  std::function<void()>& restore_func,
                                  const std::function<void()>& touch) {
    const auto type = setting.EnumIndex();

    QWidget* group = new QWidget(this);
    QHBoxLayout* layout = new QHBoxLayout(group);
    layout->setContentsMargins(0, 0, 0, 0);
    group->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) {
            QRadioButton* radio_button = new QRadioButton(name, group);
            layout->addWidget(radio_button);
            radio_buttons.push_back({id, radio_button});
        }
    } else {
        return group;
    }

    const auto get_selected = [this]() -> int {
        for (const auto& [id, button] : radio_buttons) {
            if (button->isChecked()) {
                return id;
            }
        }
        return -1;
    };

    const auto set_index = [this](u32 value) {
        for (const auto& [id, button] : radio_buttons) {
            button->setChecked(id == value);
        }
    };

    const u32 setting_value = std::strtoul(setting.ToString().c_str(), nullptr, 0);
    set_index(setting_value);

    serializer = [get_selected]() {
        int current = get_selected();
        return std::to_string(current);
    };

    restore_func = [this, set_index]() {
        const u32 global_value = std::strtoul(RelevantDefault(setting).c_str(), nullptr, 0);
        set_index(global_value);
    };

    if (!Settings::IsConfiguringGlobal()) {
        for (const auto& [id, button] : radio_buttons) {
            QObject::connect(button, &QAbstractButton::clicked, [touch]() { touch(); });
        }
    }

    return group;
}

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;
}

static void CreateIntSlider(Settings::BasicSetting& setting, bool reversed, float multiplier,
                            QLabel* feedback, const QString& use_format, QSlider* slider,
                            std::function<std::string()>& serializer,
                            std::function<void()>& restore_func) {
    const int max_val = std::strtol(setting.MaxVal().c_str(), nullptr, 0);

    const auto update_feedback = [=](int value) {
        int present = (reversed ? max_val - value : value) * multiplier + 0.5f;
        feedback->setText(use_format.arg(QVariant::fromValue(present).value<QString>()));
    };

    QObject::connect(slider, &QAbstractSlider::valueChanged, update_feedback);
    update_feedback(std::strtol(setting.ToString().c_str(), nullptr, 0));

    slider->setMinimum(std::strtol(setting.MinVal().c_str(), nullptr, 0));
    slider->setMaximum(max_val);
    slider->setValue(std::strtol(setting.ToString().c_str(), nullptr, 0));

    serializer = [slider]() { return std::to_string(slider->value()); };
    restore_func = [slider, &setting]() {
        slider->setValue(std::strtol(RelevantDefault(setting).c_str(), nullptr, 0));
    };
}

static void CreateFloatSlider(Settings::BasicSetting& setting, bool reversed, float multiplier,
                              QLabel* feedback, const QString& use_format, QSlider* slider,
                              std::function<std::string()>& serializer,
                              std::function<void()>& restore_func) {
    const float max_val = std::strtof(setting.MaxVal().c_str(), nullptr);
    const float min_val = std::strtof(setting.MinVal().c_str(), nullptr);
    const float use_multiplier =
        multiplier == default_multiplier ? default_float_multiplier : multiplier;

    const auto update_feedback = [=](float value) {
        int present = (reversed ? max_val - value : value) + 0.5f;
        feedback->setText(use_format.arg(QVariant::fromValue(present).value<QString>()));
    };

    QObject::connect(slider, &QAbstractSlider::valueChanged, update_feedback);
    update_feedback(std::strtof(setting.ToString().c_str(), nullptr));

    slider->setMinimum(min_val * use_multiplier);
    slider->setMaximum(max_val * use_multiplier);
    slider->setValue(std::strtof(setting.ToString().c_str(), nullptr) * use_multiplier);

    serializer = [slider, use_multiplier]() {
        return std::to_string(slider->value() / use_multiplier);
    };
    restore_func = [slider, &setting, use_multiplier]() {
        slider->setValue(std::strtof(RelevantDefault(setting).c_str(), nullptr) * use_multiplier);
    };
}

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);

    QString suffix = given_suffix == default_suffix ? DefaultSuffix(this, setting) : given_suffix;

    const QString use_format = QStringLiteral("%1").append(suffix);

    if (setting.IsIntegral()) {
        CreateIntSlider(setting, reversed, multiplier, feedback, use_format, slider, serializer,
                        restore_func);
    } else {
        CreateFloatSlider(setting, reversed, multiplier, feedback, use_format, slider, serializer,
                          restore_func);
    }

    slider->setInvertedAppearance(reversed);

    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 auto min_val = std::strtol(setting.MinVal().c_str(), nullptr, 0);
    const auto max_val = std::strtol(setting.MaxVal().c_str(), nullptr, 0);
    const auto default_val = std::strtol(setting.ToString().c_str(), nullptr, 0);

    QString suffix = given_suffix == default_suffix ? 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::strtol(RelevantDefault(setting).c_str(), nullptr, 0)};
        spinbox->setValue(value);
    };

    if (!Settings::IsConfiguringGlobal()) {
        QObject::connect(spinbox, QOverload<int>::of(&QSpinBox::valueChanged), [this, touch]() {
            if (spinbox->value() != std::strtol(setting.ToStringGlobal().c_str(), nullptr, 0)) {
                touch();
            }
        });
    }

    return spinbox;
}

QWidget* Widget::CreateDoubleSpinBox(const QString& given_suffix,
                                     std::function<std::string()>& serializer,
                                     std::function<void()>& restore_func,
                                     const std::function<void()>& touch) {
    const auto min_val = std::strtod(setting.MinVal().c_str(), nullptr);
    const auto max_val = std::strtod(setting.MaxVal().c_str(), nullptr);
    const auto default_val = std::strtod(setting.ToString().c_str(), nullptr);

    QString suffix = given_suffix == default_suffix ? DefaultSuffix(this, setting) : given_suffix;

    double_spinbox = new QDoubleSpinBox(this);
    double_spinbox->setRange(min_val, max_val);
    double_spinbox->setValue(default_val);
    double_spinbox->setSuffix(suffix);
    double_spinbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);

    serializer = [this]() { return fmt::format("{:f}", double_spinbox->value()); };

    restore_func = [this]() {
        auto value{std::strtod(RelevantDefault(setting).c_str(), nullptr)};
        double_spinbox->setValue(value);
    };

    if (!Settings::IsConfiguringGlobal()) {
        QObject::connect(double_spinbox, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
                         [this, touch]() {
                             if (double_spinbox->value() !=
                                 std::strtod(setting.ToStringGlobal().c_str(), nullptr)) {
                                 touch();
                             }
                         });
    }

    return double_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::strtoul(input.c_str(), nullptr, 0)));
    };

    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::strtoul(line_edit->text().toStdString().c_str(), 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::strtoll(setting.ToString().c_str(), nullptr, 0);
    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::strtoll(RelevantDefault(setting).c_str(), nullptr, 0);
            }
            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;
        case Settings::Specialization::Radio:
            return RequestType::RadioGroup;
        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()) {
        if (request == RequestType::RadioGroup) {
            data_component = CreateRadioGroup(serializer, restore_func, touch);
        } else {
            data_component = CreateCombobox(serializer, restore_func, touch);
        }
    } else if (setting.IsIntegral()) {
        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 (setting.IsFloatingPoint()) {
        switch (request) {
        case RequestType::Default:
        case RequestType::SpinBox:
            data_component = CreateDoubleSpinBox(suffix, serializer, restore_func, touch);
            break;
        case RequestType::Slider:
        case RequestType::ReverseSlider:
            data_component = CreateSlider(request == RequestType::ReverseSlider, multiplier, suffix,
                                          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->UsingGlobal()) {
                other_setting->LoadString(checkbox_serializer());
            }
            if (setting.UsingGlobal()) {
                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_->RuntimeModifiable() || !powered_on) {
            load_func();
        }
    });

    bool enable = runtime_lock || setting.RuntimeModifiable();
    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


code:
// 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;
class QDoubleSpinBox;
class QRadioButton;

namespace Settings {
class BasicSetting;
} // namespace Settings

namespace ConfigurationShared {

enum class RequestType {
    Default,
    ComboBox,
    SpinBox,
    Slider,
    ReverseSlider,
    LineEdit,
    HexEdit,
    DateTimeEdit,
    RadioGroup,
    MaxEnum,
};

constexpr float default_multiplier{1.f};
constexpr float default_float_multiplier{100.f};
static const QString default_suffix = QStringLiteral();

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 = default_multiplier,
                    Settings::BasicSetting* other_setting = nullptr,
                    const QString& suffix = default_suffix);
    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{};
    QDoubleSpinBox* double_spinbox{};
    QCheckBox* checkbox{};
    QSlider* slider{};
    QComboBox* combobox{};
    QDateTimeEdit* date_time_edit{};
    std::vector<std::pair<u32, QRadioButton*>> radio_buttons{};

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* CreateRadioGroup(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* CreateDoubleSpinBox(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 = default_multiplier,
                        Settings::BasicSetting* other_setting = nullptr,
                        const QString& suffix = default_suffix) 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 = default_suffix) 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


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#ifdef _WIN32
#include <windows.h>

#include <wincon.h>
#endif

#include "common/logging/backend.h"
#include "yuzu/debugger/console.h"
#include "yuzu/uisettings.h"

namespace Debugger {
void ToggleConsole() {
    static bool console_shown = false;
    if (console_shown == UISettings::values.show_console.GetValue()) {
        return;
    } else {
        console_shown = UISettings::values.show_console.GetValue();
    }

    using namespace Common::Log;
#if defined(_WIN32) && !defined(_DEBUG)
    FILE* temp;
    if (UISettings::values.show_console) {
        if (AllocConsole()) {
            // The first parameter for freopen_s is a out parameter, so we can just ignore it
            freopen_s(&temp, "CONIN$", "r", stdin);
            freopen_s(&temp, "CONOUT$", "w", stdout);
            freopen_s(&temp, "CONOUT$", "w", stderr);
            SetConsoleOutputCP(65001);
            SetColorConsoleBackendEnabled(true);
        }
    } else {
        if (FreeConsole()) {
            // In order to close the console, we have to also detach the streams on it.
            // Just redirect them to NUL if there is no console window
            SetColorConsoleBackendEnabled(false);
            freopen_s(&temp, "NUL", "r", stdin);
            freopen_s(&temp, "NUL", "w", stdout);
            freopen_s(&temp, "NUL", "w", stderr);
        }
    }
#else
    SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue());
#endif
}
} // namespace Debugger


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

namespace Debugger {

/**
 * Uses the WINAPI to hide or show the stderr console. This function is a placeholder until we can
 * get a real qt logging window which would work for all platforms.
 */
void ToggleConsole();
} // namespace Debugger


code:
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <QAction>
#include <QLayout>
#include <QString>
#include "common/settings.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "input_common/drivers/tas_input.h"
#include "input_common/main.h"
#include "yuzu/configuration/configure_input_player_widget.h"
#include "yuzu/debugger/controller.h"

ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_,
                                   std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
                                   QWidget* parent)
    : QWidget(parent, Qt::Dialog), hid_core{hid_core_}, input_subsystem{input_subsystem_} {
    setObjectName(QStringLiteral("Controller"));
    setWindowTitle(tr("Controller P1"));
    resize(500, 350);
    setMinimumSize(500, 350);
    // Enable the maximize button
    setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);

    widget = new PlayerControlPreview(this);
    refreshConfiguration();
    QLayout* layout = new QVBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->addWidget(widget);
    setLayout(layout);

    // Configure focus so that widget is focusable and the dialog automatically forwards focus to
    // it.
    setFocusProxy(widget);
    widget->setFocusPolicy(Qt::StrongFocus);
    widget->setFocus();
}

void ControllerDialog::refreshConfiguration() {
    UnloadController();
    auto* player_1 = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
    auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
    // Display the correct controller
    controller = handheld->IsConnected() ? handheld : player_1;

    Core::HID::ControllerUpdateCallback engine_callback{
        .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
        .is_npad_service = true,
    };
    callback_key = controller->SetCallback(engine_callback);
    widget->SetController(controller);
    is_controller_set = true;
}

QAction* ControllerDialog::toggleViewAction() {
    if (toggle_view_action == nullptr) {
        toggle_view_action = new QAction(tr("&Controller P1"), this);
        toggle_view_action->setCheckable(true);
        toggle_view_action->setChecked(isVisible());
        connect(toggle_view_action, &QAction::toggled, this, &ControllerDialog::setVisible);
    }

    return toggle_view_action;
}

void ControllerDialog::UnloadController() {
    widget->UnloadController();
    if (is_controller_set) {
        controller->DeleteCallback(callback_key);
        is_controller_set = false;
    }
}

void ControllerDialog::showEvent(QShowEvent* ev) {
    if (toggle_view_action) {
        toggle_view_action->setChecked(isVisible());
    }
    QWidget::showEvent(ev);
}

void ControllerDialog::hideEvent(QHideEvent* ev) {
    if (toggle_view_action) {
        toggle_view_action->setChecked(isVisible());
    }
    QWidget::hideEvent(ev);
}

void ControllerDialog::ControllerUpdate(Core::HID::ControllerTriggerType type) {
    // TODO(german77): Remove TAS from here
    switch (type) {
    case Core::HID::ControllerTriggerType::Button:
    case Core::HID::ControllerTriggerType::Stick: {
        const auto buttons_values = controller->GetButtonsValues();
        const auto stick_values = controller->GetSticks();
        u64 buttons = 0;
        std::size_t index = 0;
        for (const auto& button : buttons_values) {
            buttons |= button.value ? 1LLU << index : 0;
            index++;
        }
        const InputCommon::TasInput::TasAnalog left_axis = {
            .x = stick_values.left.x / 32767.f,
            .y = stick_values.left.y / 32767.f,
        };
        const InputCommon::TasInput::TasAnalog right_axis = {
            .x = stick_values.right.x / 32767.f,
            .y = stick_values.right.y / 32767.f,
        };
        input_subsystem->GetTas()->RecordInput(buttons, left_axis, right_axis);
        break;
    }
    default:
        break;
    }
}


code:
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <QWidget>

class QAction;
class QHideEvent;
class QShowEvent;
class PlayerControlPreview;

namespace InputCommon {
class InputSubsystem;
}

namespace Core::HID {
class HIDCore;
class EmulatedController;
enum class ControllerTriggerType;
} // namespace Core::HID

class ControllerDialog : public QWidget {
    Q_OBJECT

public:
    explicit ControllerDialog(Core::HID::HIDCore& hid_core_,
                              std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
                              QWidget* parent = nullptr);

    /// Returns a QAction that can be used to toggle visibility of this dialog.
    QAction* toggleViewAction();

    /// Reloads the widget to apply any changes in the configuration
    void refreshConfiguration();

    /// Disables events from the emulated controller
    void UnloadController();

protected:
    void showEvent(QShowEvent* ev) override;
    void hideEvent(QHideEvent* ev) override;

private:
    /// Redirects input from the widget to the TAS driver
    void ControllerUpdate(Core::HID::ControllerTriggerType type);

    int callback_key;
    bool is_controller_set{};
    Core::HID::EmulatedController* controller;

    QAction* toggle_view_action = nullptr;
    PlayerControlPreview* widget;
    Core::HID::HIDCore& hid_core;
    std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
};


code:
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <QAction>
#include <QLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QString>
#include <QTimer>
#include "common/common_types.h"
#include "common/microprofile.h"
#include "yuzu/debugger/profiler.h"
#include "yuzu/util/util.h"

// Include the implementation of the UI in this file. This isn't in microprofile.cpp because the
// non-Qt frontends don't need it (and don't implement the UI drawing hooks either).
#if MICROPROFILE_ENABLED
#define MICROPROFILEUI_IMPL 1
#include "common/microprofileui.h"

class MicroProfileWidget : public QWidget {
public:
    MicroProfileWidget(QWidget* parent = nullptr);

protected:
    void paintEvent(QPaintEvent* ev) override;
    void showEvent(QShowEvent* ev) override;
    void hideEvent(QHideEvent* ev) override;

    void mouseMoveEvent(QMouseEvent* ev) override;
    void mousePressEvent(QMouseEvent* ev) override;
    void mouseReleaseEvent(QMouseEvent* ev) override;
    void wheelEvent(QWheelEvent* ev) override;

    void keyPressEvent(QKeyEvent* ev) override;
    void keyReleaseEvent(QKeyEvent* ev) override;

private:
    /// This timer is used to redraw the widget's contents continuously. To save resources, it only
    /// runs while the widget is visible.
    QTimer update_timer;
    /// Scale the coordinate system appropriately when dpi != 96.
    qreal x_scale = 1.0, y_scale = 1.0;
};

#endif

MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) {
    setObjectName(QStringLiteral("MicroProfile"));
    setWindowTitle(tr("&MicroProfile"));
    resize(1000, 600);
    // Enable the maximize button
    setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);

#if MICROPROFILE_ENABLED

    MicroProfileWidget* widget = new MicroProfileWidget(this);

    QLayout* layout = new QVBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->addWidget(widget);
    setLayout(layout);

    // Configure focus so that widget is focusable and the dialog automatically forwards focus to
    // it.
    setFocusProxy(widget);
    widget->setFocusPolicy(Qt::StrongFocus);
    widget->setFocus();
#endif
}

QAction* MicroProfileDialog::toggleViewAction() {
    if (toggle_view_action == nullptr) {
        toggle_view_action = new QAction(windowTitle(), this);
        toggle_view_action->setCheckable(true);
        toggle_view_action->setChecked(isVisible());
        connect(toggle_view_action, &QAction::toggled, this, &MicroProfileDialog::setVisible);
    }

    return toggle_view_action;
}

void MicroProfileDialog::showEvent(QShowEvent* ev) {
    if (toggle_view_action) {
        toggle_view_action->setChecked(isVisible());
    }
    QWidget::showEvent(ev);
}

void MicroProfileDialog::hideEvent(QHideEvent* ev) {
    if (toggle_view_action) {
        toggle_view_action->setChecked(isVisible());
    }
    QWidget::hideEvent(ev);
}

#if MICROPROFILE_ENABLED

/// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the
/// QPainter available inside the drawing callbacks.
static QPainter* mp_painter = nullptr;

MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) {
    // Send mouse motion events even when not dragging.
    setMouseTracking(true);

    MicroProfileSetDisplayMode(1); // Timers screen
    MicroProfileInitUI();

    connect(&update_timer, &QTimer::timeout, this, qOverload<>(&MicroProfileWidget::update));
}

void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
    QPainter painter(this);

    // The units used by Microprofile for drawing are based in pixels on a 96 dpi display.
    x_scale = qreal(painter.device()->logicalDpiX()) / 96.0;
    y_scale = qreal(painter.device()->logicalDpiY()) / 96.0;
    painter.scale(x_scale, y_scale);

    painter.setBackground(Qt::black);
    painter.eraseRect(rect());

    QFont font = GetMonospaceFont();
    font.setPixelSize(MICROPROFILE_TEXT_HEIGHT);
    painter.setFont(font);

    mp_painter = &painter;
    MicroProfileDraw(rect().width() / x_scale, rect().height() / y_scale);
    mp_painter = nullptr;
}

void MicroProfileWidget::showEvent(QShowEvent* ev) {
    update_timer.start(15); // ~60 Hz
    QWidget::showEvent(ev);
}

void MicroProfileWidget::hideEvent(QHideEvent* ev) {
    update_timer.stop();
    QWidget::hideEvent(ev);
}

void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) {
    const auto mouse_position = ev->pos();
    MicroProfileMousePosition(mouse_position.x() / x_scale, mouse_position.y() / y_scale, 0);
    ev->accept();
}

void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) {
    const auto mouse_position = ev->pos();
    MicroProfileMousePosition(mouse_position.x() / x_scale, mouse_position.y() / y_scale, 0);
    MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
    ev->accept();
}

void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) {
    const auto mouse_position = ev->pos();
    MicroProfileMousePosition(mouse_position.x() / x_scale, mouse_position.y() / y_scale, 0);
    MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
    ev->accept();
}

void MicroProfileWidget::wheelEvent(QWheelEvent* ev) {
    const auto wheel_position = ev->position().toPoint();
    MicroProfileMousePosition(wheel_position.x() / x_scale, wheel_position.y() / y_scale,
                              ev->angleDelta().y() / 120);
    ev->accept();
}

void MicroProfileWidget::keyPressEvent(QKeyEvent* ev) {
    if (ev->key() == Qt::Key_Control) {
        // Inform MicroProfile that the user is holding Ctrl.
        MicroProfileModKey(1);
    }
    QWidget::keyPressEvent(ev);
}

void MicroProfileWidget::keyReleaseEvent(QKeyEvent* ev) {
    if (ev->key() == Qt::Key_Control) {
        MicroProfileModKey(0);
    }
    QWidget::keyReleaseEvent(ev);
}

// These functions are called by MicroProfileDraw to draw the interface elements on the screen.

void MicroProfileDrawText(int x, int y, u32 hex_color, const char* text, u32 text_length) {
    // hex_color does not include an alpha, so it must be assumed to be 255
    mp_painter->setPen(QColor::fromRgb(hex_color));

    // It's impossible to draw a string using a monospaced font with a fixed width per cell in a
    // way that's reliable across different platforms and fonts as far as I (yuriks) can tell, so
    // draw each character individually in order to precisely control the text advance.
    for (u32 i = 0; i < text_length; ++i) {
        // Position the text baseline 1 pixel above the bottom of the text cell, this gives nice
        // vertical alignment of text for a wide range of tested fonts.
        mp_painter->drawText(x, y + MICROPROFILE_TEXT_HEIGHT - 2, QString{QLatin1Char{text[i]}});
        x += MICROPROFILE_TEXT_WIDTH + 1;
    }
}

void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color,
                         MicroProfileBoxType type) {
    QColor color = QColor::fromRgba(hex_color);
    QBrush brush = color;
    if (type == MicroProfileBoxTypeBar) {
        QLinearGradient gradient(left, top, left, bottom);
        gradient.setColorAt(0.f, color.lighter(125));
        gradient.setColorAt(1.f, color.darker(125));
        brush = gradient;
    }
    mp_painter->fillRect(left, top, right - left, bottom - top, brush);
}

void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color) {
    // Temporary vector used to convert between the float array and QPointF. Marked static to reuse
    // the allocation across calls.
    static std::vector<QPointF> point_buf;

    for (u32 i = 0; i < vertices_length; ++i) {
        point_buf.emplace_back(vertices[i * 2 + 0], vertices[i * 2 + 1]);
    }

    // hex_color does not include an alpha, so it must be assumed to be 255
    mp_painter->setPen(QColor::fromRgb(hex_color));
    mp_painter->drawPolyline(point_buf.data(), vertices_length);
    point_buf.clear();
}
#endif


code:
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <QWidget>

class QAction;
class QHideEvent;
class QShowEvent;

class MicroProfileDialog : public QWidget {
    Q_OBJECT

public:
    explicit MicroProfileDialog(QWidget* parent = nullptr);

    /// Returns a QAction that can be used to toggle visibility of this dialog.
    QAction* toggleViewAction();

protected:
    void showEvent(QShowEvent* ev) override;
    void hideEvent(QHideEvent* ev) override;

private:
    QAction* toggle_view_action = nullptr;
};