diff options
Diffstat (limited to 'src/yuzu/configuration')
49 files changed, 11249 insertions, 2794 deletions
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 1d4816d34..3c423a271 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -5,8 +5,8 @@ #include <array> #include <QKeySequence> #include <QSettings> +#include "common/common_paths.h" #include "common/file_util.h" -#include "configure_input_simple.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/hid/controllers/npad.h" #include "input_common/main.h" @@ -15,14 +15,10 @@ namespace FS = Common::FS; -Config::Config(const std::string& config_file, bool is_global) { - // TODO: Don't hardcode the path; let the frontend decide where to put the config files. - qt_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + config_file; - FS::CreateFullPath(qt_config_loc); - qt_config = - std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); - global = is_global; - Reload(); +Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) { + global = config_type == ConfigType::GlobalConfig; + + Initialize(config_name); } Config::~Config() { @@ -32,29 +28,36 @@ Config::~Config() { } const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { - Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, - Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, - Qt::Key_H, Qt::Key_G, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, Qt::Key_Down, Qt::Key_J, - Qt::Key_I, Qt::Key_L, Qt::Key_K, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V, + Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, + Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, + Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V, +}; + +const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = { + Qt::Key_7, + Qt::Key_8, }; -const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ +const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ { Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, - Qt::Key_E, }, { Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L, - Qt::Key_R, }, }}; +const std::array<int, 2> Config::default_stick_mod = { + Qt::Key_E, + Qt::Key_R, +}; + const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons = { Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal, @@ -236,76 +239,154 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ }}; // clang-format on -void Config::ReadPlayerValues() { - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { - auto& player = Settings::values.players[p]; +void Config::Initialize(const std::string& config_name) { + switch (type) { + case ConfigType::GlobalConfig: + qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir), + config_name); + FS::CreateFullPath(qt_config_loc); + qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), + QSettings::IniFormat); + Reload(); + break; + case ConfigType::PerGameConfig: + qt_config_loc = fmt::format("{}custom" DIR_SEP "{}.ini", + FS::GetUserPath(FS::UserPath::ConfigDir), config_name); + FS::CreateFullPath(qt_config_loc); + qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), + QSettings::IniFormat); + Reload(); + break; + case ConfigType::InputProfile: + qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini", + FS::GetUserPath(FS::UserPath::ConfigDir), config_name); + FS::CreateFullPath(qt_config_loc); + qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), + QSettings::IniFormat); + break; + } +} + +void Config::ReadPlayerValue(std::size_t player_index) { + const QString player_prefix = [this, player_index] { + if (type == ConfigType::InputProfile) { + return QString{}; + } else { + return QStringLiteral("player_%1_").arg(player_index); + } + }(); + auto& player = Settings::values.players.GetValue()[player_index]; + + if (player_prefix.isEmpty()) { + const auto controller = static_cast<Settings::ControllerType>( + qt_config + ->value(QStringLiteral("%1type").arg(player_prefix), + static_cast<u8>(Settings::ControllerType::ProController)) + .toUInt()); + + if (controller == Settings::ControllerType::LeftJoycon || + controller == Settings::ControllerType::RightJoycon) { + player.controller_type = controller; + } + } else { player.connected = - ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool(); + ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0) + .toBool(); - player.type = static_cast<Settings::ControllerType>( + player.controller_type = static_cast<Settings::ControllerType>( qt_config - ->value(QStringLiteral("player_%1_type").arg(p), - static_cast<u8>(Settings::ControllerType::DualJoycon)) + ->value(QStringLiteral("%1type").arg(player_prefix), + static_cast<u8>(Settings::ControllerType::ProController)) .toUInt()); + player.vibration_enabled = + qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true) + .toBool(); + + player.vibration_strength = + qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100) + .toInt(); + player.body_color_left = qt_config - ->value(QStringLiteral("player_%1_body_color_left").arg(p), + ->value(QStringLiteral("%1body_color_left").arg(player_prefix), Settings::JOYCON_BODY_NEON_BLUE) .toUInt(); - player.body_color_right = qt_config - ->value(QStringLiteral("player_%1_body_color_right").arg(p), - Settings::JOYCON_BODY_NEON_RED) - .toUInt(); - player.button_color_left = qt_config - ->value(QStringLiteral("player_%1_button_color_left").arg(p), - Settings::JOYCON_BUTTONS_NEON_BLUE) - .toUInt(); + player.body_color_right = + qt_config + ->value(QStringLiteral("%1body_color_right").arg(player_prefix), + Settings::JOYCON_BODY_NEON_RED) + .toUInt(); + player.button_color_left = + qt_config + ->value(QStringLiteral("%1button_color_left").arg(player_prefix), + Settings::JOYCON_BUTTONS_NEON_BLUE) + .toUInt(); player.button_color_right = qt_config - ->value(QStringLiteral("player_%1_button_color_right").arg(p), + ->value(QStringLiteral("%1button_color_right").arg(player_prefix), Settings::JOYCON_BUTTONS_NEON_RED) .toUInt(); + } - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_buttons[i]); - auto& player_buttons = player.buttons[i]; - - player_buttons = qt_config - ->value(QStringLiteral("player_%1_").arg(p) + - QString::fromUtf8(Settings::NativeButton::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_buttons.empty()) { - player_buttons = default_param; - } + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& player_buttons = player.buttons[i]; + + player_buttons = qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeButton::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_buttons.empty()) { + player_buttons = default_param; } + } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_analogs[i][4], 0.5f); - auto& player_analogs = player.analogs[i]; - - player_analogs = qt_config - ->value(QStringLiteral("player_%1_").arg(p) + - QString::fromUtf8(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_analogs.empty()) { - player_analogs = default_param; - } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + auto& player_analogs = player.analogs[i]; + + player_analogs = qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeAnalog::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_analogs.empty()) { + player_analogs = default_param; } } - std::stable_partition( - Settings::values.players.begin(), - Settings::values.players.begin() + - Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD), - [](const auto& player) { return player.connected; }); + for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) { + auto& player_vibrations = player.vibrations[i]; + + player_vibrations = + qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeVibration::mapping[i]), + QString{}) + .toString() + .toStdString(); + } + + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + auto& player_motions = player.motions[i]; + + player_motions = qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeMotion::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_motions.empty()) { + player_motions = default_param; + } + } } void Config::ReadDebugValues() { @@ -330,7 +411,7 @@ void Config::ReadDebugValues() { for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_analogs[i][4], 0.5f); + default_analogs[i][3], default_stick_mod[i], 0.5f); auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; debug_pad_analogs = qt_config @@ -397,13 +478,6 @@ void Config::ReadTouchscreenValues() { ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); } -void Config::ApplyDefaultProfileIfInputInvalid() { - if (!std::any_of(Settings::values.players.begin(), Settings::values.players.end(), - [](const Settings::PlayerInput& in) { return in.connected; })) { - ApplyInputProfileConfiguration(UISettings::values.profile_index); - } -} - void Config::ReadAudioValues() { qt_config->beginGroup(QStringLiteral("Audio")); @@ -427,17 +501,73 @@ void Config::ReadAudioValues() { void Config::ReadControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); - ReadPlayerValues(); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + ReadPlayerValue(p); + } ReadDebugValues(); ReadKeyboardValues(); ReadMouseValues(); ReadTouchscreenValues(); + ReadMotionTouchValues(); + + ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false); + ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), + true); + ReadSettingGlobal(Settings::values.enable_accurate_vibrations, + QStringLiteral("enable_accurate_vibrations"), false); + ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true); + + qt_config->endGroup(); +} + +void Config::ReadMotionTouchValues() { + int num_touch_from_button_maps = + qt_config->beginReadArray(QStringLiteral("touch_from_button_maps")); + + if (num_touch_from_button_maps > 0) { + const auto append_touch_from_button_map = [this] { + Settings::TouchFromButtonMap map; + map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default")) + .toString() + .toStdString(); + const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries")); + map.buttons.reserve(num_touch_maps); + for (int i = 0; i < num_touch_maps; i++) { + qt_config->setArrayIndex(i); + std::string touch_mapping = + ReadSetting(QStringLiteral("bind")).toString().toStdString(); + map.buttons.emplace_back(std::move(touch_mapping)); + } + qt_config->endArray(); // entries + Settings::values.touch_from_button_maps.emplace_back(std::move(map)); + }; + + for (int i = 0; i < num_touch_from_button_maps; ++i) { + qt_config->setArrayIndex(i); + append_touch_from_button_map(); + } + } else { + Settings::values.touch_from_button_maps.emplace_back( + Settings::TouchFromButtonMap{"default", {}}); + num_touch_from_button_maps = 1; + } + qt_config->endArray(); Settings::values.motion_device = ReadSetting(QStringLiteral("motion_device"), QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")) .toString() .toStdString(); + Settings::values.touch_device = + ReadSetting(QStringLiteral("touch_device"), QStringLiteral("engine:emu_window")) + .toString() + .toStdString(); + Settings::values.use_touch_from_button = + ReadSetting(QStringLiteral("use_touch_from_button"), false).toBool(); + Settings::values.touch_from_button_map_index = + ReadSetting(QStringLiteral("touch_from_button_map"), 0).toInt(); + Settings::values.touch_from_button_map_index = + std::clamp(Settings::values.touch_from_button_map_index, 0, num_touch_from_button_maps - 1); Settings::values.udp_input_address = ReadSetting(QStringLiteral("udp_input_address"), QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR)) @@ -448,16 +578,12 @@ void Config::ReadControlValues() { .toInt()); Settings::values.udp_pad_index = static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt()); - Settings::values.use_docked_mode = - ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); - - qt_config->endGroup(); } void Config::ReadCoreValues() { qt_config->beginGroup(QStringLiteral("Core")); - ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false); + ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), true); qt_config->endGroup(); } @@ -501,7 +627,7 @@ void Config::ReadDataStorageValues() { Settings::values.gamecard_current_game = ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool(); Settings::values.gamecard_path = - ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString(); + ReadSetting(QStringLiteral("gamecard_path"), QString{}).toString().toStdString(); qt_config->endGroup(); } @@ -515,7 +641,7 @@ void Config::ReadDebuggingValues() { Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); Settings::values.program_args = - ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); + ReadSetting(QStringLiteral("program_args"), QString{}).toString().toStdString(); Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); Settings::values.reporting_services = @@ -550,8 +676,7 @@ void Config::ReadDisabledAddOnValues() { const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled")); for (int j = 0; j < d_size; ++j) { qt_config->setArrayIndex(j); - out.push_back( - ReadSetting(QStringLiteral("d"), QStringLiteral("")).toString().toStdString()); + out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString()); } qt_config->endArray(); Settings::values.disabled_addons.insert_or_assign(title_id, out); @@ -661,10 +786,12 @@ void Config::ReadRendererValues() { QStringLiteral("use_disk_shader_cache"), true); ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0); ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation, - QStringLiteral("use_asynchronous_gpu_emulation"), false); + QStringLiteral("use_asynchronous_gpu_emulation"), true); + ReadSettingGlobal(Settings::values.use_nvdec_emulation, QStringLiteral("use_nvdec_emulation"), + true); ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true); ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"), - false); + true); ReadSettingGlobal(Settings::values.use_asynchronous_shaders, QStringLiteral("use_asynchronous_shaders"), false); ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), @@ -790,14 +917,11 @@ void Config::ReadUIValues() { UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool(); UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt(); UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool(); - UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt(); UISettings::values.pause_when_in_background = ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool(); UISettings::values.hide_mouse = ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool(); - ApplyDefaultProfileIfInputInvalid(); - qt_config->endGroup(); } @@ -866,40 +990,64 @@ void Config::ReadValues() { ReadSystemValues(); } -void Config::SavePlayerValues() { - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { - const auto& player = Settings::values.players[p]; +void Config::SavePlayerValue(std::size_t player_index) { + const QString player_prefix = [this, player_index] { + if (type == ConfigType::InputProfile) { + return QString{}; + } else { + return QStringLiteral("player_%1_").arg(player_index); + } + }(); - WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false); - WriteSetting(QStringLiteral("player_%1_type").arg(p), static_cast<u8>(player.type), - static_cast<u8>(Settings::ControllerType::DualJoycon)); + const auto& player = Settings::values.players.GetValue()[player_index]; - WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left, + WriteSetting(QStringLiteral("%1type").arg(player_prefix), + static_cast<u8>(player.controller_type), + static_cast<u8>(Settings::ControllerType::ProController)); + + if (!player_prefix.isEmpty()) { + WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false); + WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix), + player.vibration_enabled, true); + WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix), + player.vibration_strength, 100); + WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left, Settings::JOYCON_BODY_NEON_BLUE); - WriteSetting(QStringLiteral("player_%1_body_color_right").arg(p), player.body_color_right, - Settings::JOYCON_BODY_NEON_RED); - WriteSetting(QStringLiteral("player_%1_button_color_left").arg(p), player.button_color_left, - Settings::JOYCON_BUTTONS_NEON_BLUE); - WriteSetting(QStringLiteral("player_%1_button_color_right").arg(p), + WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix), + player.body_color_right, Settings::JOYCON_BODY_NEON_RED); + WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix), + player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE); + WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix), player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED); + } - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_buttons[i]); - WriteSetting(QStringLiteral("player_%1_").arg(p) + - QString::fromStdString(Settings::NativeButton::mapping[i]), - QString::fromStdString(player.buttons[i]), - QString::fromStdString(default_param)); - } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_analogs[i][4], 0.5f); - WriteSetting(QStringLiteral("player_%1_").arg(p) + - QString::fromStdString(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(player.analogs[i]), - QString::fromStdString(default_param)); - } + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeButton::mapping[i]), + QString::fromStdString(player.buttons[i]), + QString::fromStdString(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeAnalog::mapping[i]), + QString::fromStdString(player.analogs[i]), + QString::fromStdString(default_param)); + } + for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) { + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeVibration::mapping[i]), + QString::fromStdString(player.vibrations[i]), QString{}); + } + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeMotion::mapping[i]), + QString::fromStdString(player.motions[i]), + QString::fromStdString(default_param)); } } @@ -915,7 +1063,7 @@ void Config::SaveDebugValues() { for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_analogs[i][4], 0.5f); + default_analogs[i][3], default_stick_mod[i], 0.5f); WriteSetting(QStringLiteral("debug_pad_") + QString::fromStdString(Settings::NativeAnalog::mapping[i]), QString::fromStdString(Settings::values.debug_pad_analogs[i]), @@ -949,6 +1097,43 @@ void Config::SaveTouchscreenValues() { WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); } +void Config::SaveMotionTouchValues() { + WriteSetting(QStringLiteral("motion_device"), + QString::fromStdString(Settings::values.motion_device), + QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); + WriteSetting(QStringLiteral("touch_device"), + QString::fromStdString(Settings::values.touch_device), + QStringLiteral("engine:emu_window")); + WriteSetting(QStringLiteral("use_touch_from_button"), Settings::values.use_touch_from_button, + false); + WriteSetting(QStringLiteral("touch_from_button_map"), + Settings::values.touch_from_button_map_index, 0); + WriteSetting(QStringLiteral("udp_input_address"), + QString::fromStdString(Settings::values.udp_input_address), + QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR)); + WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port, + InputCommon::CemuhookUDP::DEFAULT_PORT); + WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0); + + 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)); + WriteSetting(QStringLiteral("name"), + QString::fromStdString(Settings::values.touch_from_button_maps[p].name), + QStringLiteral("default")); + qt_config->beginWriteArray(QStringLiteral("entries")); + for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); + ++q) { + qt_config->setArrayIndex(static_cast<int>(q)); + WriteSetting( + QStringLiteral("bind"), + QString::fromStdString(Settings::values.touch_from_button_maps[p].buttons[q])); + } + qt_config->endArray(); + } + qt_config->endArray(); +} + void Config::SaveValues() { if (global) { SaveControlValues(); @@ -987,22 +1172,27 @@ void Config::SaveAudioValues() { void Config::SaveControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); - SavePlayerValues(); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + SavePlayerValue(p); + } SaveDebugValues(); SaveMouseValues(); SaveTouchscreenValues(); + SaveMotionTouchValues(); + WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); + WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, + true); + WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"), + Settings::values.enable_accurate_vibrations, false); + WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true); WriteSetting(QStringLiteral("motion_device"), QString::fromStdString(Settings::values.motion_device), QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); + WriteSetting(QStringLiteral("touch_device"), + QString::fromStdString(Settings::values.touch_device), + QStringLiteral("engine:emu_window")); WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); - WriteSetting(QStringLiteral("udp_input_address"), - QString::fromStdString(Settings::values.udp_input_address), - QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR)); - WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port, - InputCommon::CemuhookUDP::DEFAULT_PORT); - WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0); - WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); qt_config->endGroup(); } @@ -1010,7 +1200,7 @@ void Config::SaveControlValues() { void Config::SaveCoreValues() { qt_config->beginGroup(QStringLiteral("Core")); - WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false); + WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, true); qt_config->endGroup(); } @@ -1038,7 +1228,7 @@ void Config::SaveDataStorageValues() { WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, false); WriteSetting(QStringLiteral("gamecard_path"), - QString::fromStdString(Settings::values.gamecard_path), QStringLiteral("")); + QString::fromStdString(Settings::values.gamecard_path), QString{}); qt_config->endGroup(); } @@ -1051,7 +1241,7 @@ void Config::SaveDebuggingValues() { WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); WriteSetting(QStringLiteral("program_args"), - QString::fromStdString(Settings::values.program_args), QStringLiteral("")); + QString::fromStdString(Settings::values.program_args), QString{}); WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); @@ -1078,8 +1268,7 @@ void Config::SaveDisabledAddOnValues() { qt_config->beginWriteArray(QStringLiteral("disabled")); for (std::size_t j = 0; j < elem.second.size(); ++j) { qt_config->setArrayIndex(static_cast<int>(j)); - WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), - QStringLiteral("")); + WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{}); } qt_config->endArray(); ++i; @@ -1167,10 +1356,12 @@ void Config::SaveRendererValues() { static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)), Settings::values.gpu_accuracy.UsingGlobal(), 0); WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"), - Settings::values.use_asynchronous_gpu_emulation, false); + Settings::values.use_asynchronous_gpu_emulation, true); + WriteSettingGlobal(QStringLiteral("use_nvdec_emulation"), Settings::values.use_nvdec_emulation, + true); WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); WriteSettingGlobal(QStringLiteral("use_assembly_shaders"), - Settings::values.use_assembly_shaders, false); + Settings::values.use_assembly_shaders, true); WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"), Settings::values.use_asynchronous_shaders, false); WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, @@ -1268,7 +1459,6 @@ void Config::SaveUIValues() { WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true); WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0); WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false); - WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0); WriteSetting(QStringLiteral("pauseWhenInBackground"), UISettings::values.pause_when_in_background, false); WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false); @@ -1415,3 +1605,19 @@ void Config::Save() { Settings::Sanitize(); SaveValues(); } + +void Config::ReadControlPlayerValue(std::size_t player_index) { + qt_config->beginGroup(QStringLiteral("Controls")); + ReadPlayerValue(player_index); + qt_config->endGroup(); +} + +void Config::SaveControlPlayerValue(std::size_t player_index) { + qt_config->beginGroup(QStringLiteral("Controls")); + SavePlayerValue(player_index); + qt_config->endGroup(); +} + +const std::string& Config::GetConfigFilePath() const { + return qt_config_loc; +} diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index e5f39b040..8a600e19d 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -16,14 +16,28 @@ class QSettings; class Config { public: - explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true); + enum class ConfigType { + GlobalConfig, + PerGameConfig, + InputProfile, + }; + + explicit Config(const std::string& config_name = "qt-config", + ConfigType config_type = ConfigType::GlobalConfig); ~Config(); void Reload(); void Save(); + void ReadControlPlayerValue(std::size_t player_index); + void SaveControlPlayerValue(std::size_t player_index); + + const std::string& GetConfigFilePath() const; + static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; - static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; + static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; + static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; + static const std::array<int, 2> default_stick_mod; static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons; static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; @@ -31,13 +45,15 @@ public: static const std::array<UISettings::Shortcut, 16> default_hotkeys; private: + void Initialize(const std::string& config_name); + void ReadValues(); - void ReadPlayerValues(); + void ReadPlayerValue(std::size_t player_index); void ReadDebugValues(); void ReadKeyboardValues(); void ReadMouseValues(); void ReadTouchscreenValues(); - void ApplyDefaultProfileIfInputInvalid(); + void ReadMotionTouchValues(); // Read functions bases off the respective config section names. void ReadAudioValues(); @@ -60,10 +76,11 @@ private: void ReadWebServiceValues(); void SaveValues(); - void SavePlayerValues(); + void SavePlayerValue(std::size_t player_index); void SaveDebugValues(); void SaveMouseValues(); void SaveTouchscreenValues(); + void SaveMotionTouchValues(); // Save functions based off the respective config section names. void SaveAudioValues(); @@ -108,9 +125,9 @@ private: void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, const QVariant& default_value); + ConfigType type; std::unique_ptr<QSettings> qt_config; std::string qt_config_loc; - bool global; }; diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index 5f5d8e571..f92c3aff3 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>382</width> + <width>650</width> <height>650</height> </rect> </property> @@ -26,13 +26,13 @@ <widget class="QListWidget" name="selectorList"> <property name="minimumSize"> <size> - <width>150</width> + <width>120</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> - <width>150</width> + <width>120</width> <height>16777215</height> </size> </property> @@ -44,76 +44,121 @@ <number>0</number> </property> <widget class="ConfigureGeneral" name="generalTab"> + <property name="accessibleName"> + <string>General</string> + </property> <attribute name="title"> <string>General</string> </attribute> </widget> <widget class="ConfigureUi" name="uiTab"> + <property name="accessibleName"> + <string>UI</string> + </property> <attribute name="title"> <string>Game List</string> </attribute> </widget> <widget class="ConfigureSystem" name="systemTab"> + <property name="accessibleName"> + <string>System</string> + </property> <attribute name="title"> <string>System</string> </attribute> </widget> <widget class="ConfigureProfileManager" name="profileManagerTab"> + <property name="accessibleName"> + <string>Profiles</string> + </property> <attribute name="title"> <string>Profiles</string> </attribute> </widget> <widget class="ConfigureFilesystem" name="filesystemTab"> + <property name="accessibleName"> + <string>Filesystem</string> + </property> <attribute name="title"> <string>Filesystem</string> </attribute> </widget> - <widget class="ConfigureInputSimple" name="inputTab"> + <widget class="ConfigureInput" name="inputTab"> + <property name="accessibleName"> + <string>Controls</string> + </property> <attribute name="title"> - <string>Input</string> + <string>Controls</string> </attribute> </widget> <widget class="ConfigureHotkeys" name="hotkeysTab"> + <property name="accessibleName"> + <string>Hotkeys</string> + </property> <attribute name="title"> <string>Hotkeys</string> </attribute> </widget> <widget class="ConfigureCpu" name="cpuTab"> + <property name="accessibleName"> + <string>CPU</string> + </property> <attribute name="title"> <string>CPU</string> </attribute> </widget> <widget class="ConfigureCpuDebug" name="cpuDebugTab"> + <property name="accessibleName"> + <string>Debug</string> + </property> <attribute name="title"> <string>Debug</string> </attribute> </widget> <widget class="ConfigureGraphics" name="graphicsTab"> + <property name="accessibleName"> + <string>Graphics</string> + </property> <attribute name="title"> <string>Graphics</string> </attribute> </widget> <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab"> + <property name="accessibleName"> + <string>Advanced</string> + </property> <attribute name="title"> <string>GraphicsAdvanced</string> </attribute> </widget> <widget class="ConfigureAudio" name="audioTab"> + <property name="accessibleName"> + <string>Audio</string> + </property> <attribute name="title"> <string>Audio</string> </attribute> </widget> <widget class="ConfigureDebug" name="debugTab"> + <property name="accessibleName"> + <string>Debug</string> + </property> <attribute name="title"> <string>Debug</string> </attribute> </widget> <widget class="ConfigureWeb" name="webTab"> + <property name="accessibleName"> + <string>Web</string> + </property> <attribute name="title"> <string>Web</string> </attribute> </widget> <widget class="ConfigureService" name="serviceTab"> + <property name="accessibleName"> + <string>Services</string> + </property> <attribute name="title"> <string>Services</string> </attribute> @@ -205,9 +250,9 @@ <container>1</container> </customwidget> <customwidget> - <class>ConfigureInputSimple</class> + <class>ConfigureInput</class> <extends>QWidget</extends> - <header>configuration/configure_input_simple.h</header> + <header>configuration/configure_input.h</header> <container>1</container> </customwidget> <customwidget> @@ -230,32 +275,12 @@ <signal>accepted()</signal> <receiver>ConfigureDialog</receiver> <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>220</x> - <y>380</y> - </hint> - <hint type="destinationlabel"> - <x>220</x> - <y>200</y> - </hint> - </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>ConfigureDialog</receiver> <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>220</x> - <y>380</y> - </hint> - <hint type="destinationlabel"> - <x>220</x> - <y>200</y> - </hint> - </hints> </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index fa9124ecf..db9518798 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -25,8 +25,8 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, &ConfigureAudio::UpdateAudioDevices); - ui->volume_label->setVisible(Settings::configuring_global); - ui->volume_combo_box->setVisible(!Settings::configuring_global); + ui->volume_label->setVisible(Settings::IsConfiguringGlobal()); + ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal()); SetupPerGameUI(); @@ -51,7 +51,7 @@ void ConfigureAudio::SetConfiguration() { ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue()); - if (!Settings::configuring_global) { + if (!Settings::IsConfiguringGlobal()) { if (Settings::values.volume.UsingGlobal()) { ui->volume_combo_box->setCurrentIndex(0); ui->volume_slider->setEnabled(false); @@ -99,7 +99,7 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) { } void ConfigureAudio::ApplyConfiguration() { - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) .toStdString(); @@ -165,7 +165,7 @@ void ConfigureAudio::RetranslateUI() { } void ConfigureAudio::SetupPerGameUI() { - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal()); ui->toggle_audio_stretching->setEnabled( Settings::values.enable_audio_stretching.UsingGlobal()); diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp new file mode 100644 index 000000000..a878ef9c6 --- /dev/null +++ b/src/yuzu/configuration/configure_debug_controller.cpp @@ -0,0 +1,43 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "ui_configure_debug_controller.h" +#include "yuzu/configuration/configure_debug_controller.h" +#include "yuzu/configuration/configure_input_player.h" + +ConfigureDebugController::ConfigureDebugController(QWidget* parent, + InputCommon::InputSubsystem* input_subsystem, + InputProfiles* profiles) + : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()), + debug_controller( + new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, true)) { + ui->setupUi(this); + + ui->controllerLayout->addWidget(debug_controller); + + connect(ui->clear_all_button, &QPushButton::clicked, this, + [this] { debug_controller->ClearAll(); }); + connect(ui->restore_defaults_button, &QPushButton::clicked, this, + [this] { debug_controller->RestoreDefaults(); }); + + RetranslateUI(); +} + +ConfigureDebugController::~ConfigureDebugController() = default; + +void ConfigureDebugController::ApplyConfiguration() { + debug_controller->ApplyConfiguration(); +} + +void ConfigureDebugController::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QDialog::changeEvent(event); +} + +void ConfigureDebugController::RetranslateUI() { + ui->retranslateUi(this); +} diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h new file mode 100644 index 000000000..b4f53fad5 --- /dev/null +++ b/src/yuzu/configuration/configure_debug_controller.h @@ -0,0 +1,41 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QDialog> + +class QPushButton; + +class ConfigureInputPlayer; + +class InputProfiles; + +namespace InputCommon { +class InputSubsystem; +} + +namespace Ui { +class ConfigureDebugController; +} + +class ConfigureDebugController : public QDialog { + Q_OBJECT + +public: + explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem, + InputProfiles* profiles); + ~ConfigureDebugController() override; + + void ApplyConfiguration(); + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + std::unique_ptr<Ui::ConfigureDebugController> ui; + + ConfigureInputPlayer* debug_controller; +}; diff --git a/src/yuzu/configuration/configure_debug_controller.ui b/src/yuzu/configuration/configure_debug_controller.ui new file mode 100644 index 000000000..7b7e6582c --- /dev/null +++ b/src/yuzu/configuration/configure_debug_controller.ui @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureDebugController</class> + <widget class="QDialog" name="ConfigureDebugController"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>780</width> + <height>500</height> + </rect> + </property> + <property name="windowTitle"> + <string>Configure Debug Controller</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>2</number> + </property> + <property name="leftMargin"> + <number>9</number> + </property> + <property name="topMargin"> + <number>9</number> + </property> + <property name="rightMargin"> + <number>9</number> + </property> + <property name="bottomMargin"> + <number>9</number> + </property> + <item> + <layout class="QHBoxLayout" name="controllerLayout"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="clear_all_button"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="restore_defaults_button"> + <property name="text"> + <string>Defaults</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> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ConfigureDebugController</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ConfigureDebugController</receiver> + <slot>reject()</slot> + </connection> + </connections> +</ui> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 4e30dc51e..5041e0bf8 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -12,14 +12,17 @@ #include "yuzu/configuration/configure_input_player.h" #include "yuzu/hotkeys.h" -ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) +ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, + InputCommon::InputSubsystem* input_subsystem) : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { - Settings::configuring_global = true; + Settings::SetConfiguringGlobal(true); ui->setupUi(this); ui->hotkeysTab->Populate(registry); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + ui->inputTab->Initialize(input_subsystem); + SetConfiguration(); PopulateSelectionList(); @@ -80,12 +83,12 @@ Q_DECLARE_METATYPE(QList<QWidget*>); void ConfigureDialog::PopulateSelectionList() { const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ - {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}}, + {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}}, {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}}, {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, {tr("Audio"), {ui->audioTab}}, - {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, + {tr("Controls"), ui->inputTab->GetSubTabs()}}, }; [[maybe_unused]] const QSignalBlocker blocker(ui->selectorList); @@ -117,7 +120,7 @@ void ConfigureDialog::UpdateVisibleTabs() { {ui->generalTab, tr("General")}, {ui->systemTab, tr("System")}, {ui->profileManagerTab, tr("Profiles")}, - {ui->inputTab, tr("Input")}, + {ui->inputTab, tr("Controls")}, {ui->hotkeysTab, tr("Hotkeys")}, {ui->cpuTab, tr("CPU")}, {ui->cpuDebugTab, tr("Debug")}, @@ -138,6 +141,6 @@ void ConfigureDialog::UpdateVisibleTabs() { const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole)); for (const auto tab : tabs) { - ui->tabWidget->addTab(tab, widgets.at(tab)); + ui->tabWidget->addTab(tab, tab->accessibleName()); } } diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 4289bc225..570c3b941 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -9,6 +9,10 @@ class HotkeyRegistry; +namespace InputCommon { +class InputSubsystem; +} + namespace Ui { class ConfigureDialog; } @@ -17,7 +21,8 @@ class ConfigureDialog : public QDialog { Q_OBJECT public: - explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry); + explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, + InputCommon::InputSubsystem* input_subsystem); ~ConfigureDialog() override; void ApplyConfiguration(); diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 830096ea0..d4d29d422 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -19,7 +19,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) SetConfiguration(); - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() { ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); }); } @@ -41,7 +41,7 @@ void ConfigureGeneral::SetConfiguration() { ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue()); ui->frame_limit->setValue(Settings::values.frame_limit.GetValue()); - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue()); } else { ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue() && @@ -50,7 +50,7 @@ void ConfigureGeneral::SetConfiguration() { } void ConfigureGeneral::ApplyConfiguration() { - if (Settings::configuring_global) { + 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(); @@ -93,7 +93,7 @@ void ConfigureGeneral::RetranslateUI() { } void ConfigureGeneral::SetupPerGameUI() { - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal()); ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal()); diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 07d818548..6fda0ce35 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -33,7 +33,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] { UpdateDeviceComboBox(); - if (!Settings::configuring_global) { + if (!Settings::IsConfiguringGlobal()) { ConfigurationShared::SetHighlight( ui->api_layout, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX); } @@ -49,8 +49,8 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) UpdateBackgroundColorButton(new_bg_color); }); - ui->bg_label->setVisible(Settings::configuring_global); - ui->bg_combobox->setVisible(!Settings::configuring_global); + ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); + ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); } void ConfigureGraphics::UpdateDeviceSelection(int device) { @@ -70,11 +70,13 @@ void ConfigureGraphics::SetConfiguration() { ui->api->setEnabled(runtime_lock); ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); ui->use_disk_shader_cache->setEnabled(runtime_lock); + ui->use_nvdec_emulation->setEnabled(runtime_lock); 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->use_nvdec_emulation->setChecked(Settings::values.use_nvdec_emulation.GetValue()); - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); } else { @@ -98,7 +100,7 @@ void ConfigureGraphics::SetConfiguration() { } void ConfigureGraphics::ApplyConfiguration() { - if (Settings::configuring_global) { + 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()); @@ -116,6 +118,9 @@ void ConfigureGraphics::ApplyConfiguration() { Settings::values.use_asynchronous_gpu_emulation.SetValue( ui->use_asynchronous_gpu_emulation->isChecked()); } + if (Settings::values.use_nvdec_emulation.UsingGlobal()) { + Settings::values.use_nvdec_emulation.SetValue(ui->use_nvdec_emulation->isChecked()); + } if (Settings::values.bg_red.UsingGlobal()) { Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF())); Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF())); @@ -144,6 +149,8 @@ void ConfigureGraphics::ApplyConfiguration() { ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation, ui->use_asynchronous_gpu_emulation, use_asynchronous_gpu_emulation); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_nvdec_emulation, + ui->use_nvdec_emulation, use_nvdec_emulation); if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { Settings::values.bg_red.SetGlobal(true); @@ -187,7 +194,7 @@ void ConfigureGraphics::UpdateDeviceComboBox() { bool enabled = false; - if (!Settings::configuring_global && + if (!Settings::IsConfiguringGlobal() && ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { vulkan_device = Settings::values.vulkan_device.GetValue(); } @@ -205,7 +212,7 @@ void ConfigureGraphics::UpdateDeviceComboBox() { break; } // If in per-game config and use global is selected, don't enable. - enabled &= !(!Settings::configuring_global && + enabled &= !(!Settings::IsConfiguringGlobal() && ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX); ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); } @@ -220,7 +227,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() { } Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); } @@ -234,12 +241,13 @@ Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { } void ConfigureGraphics::SetupPerGameUI() { - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal()); ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal()); ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); ui->use_asynchronous_gpu_emulation->setEnabled( Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); + ui->use_nvdec_emulation->setEnabled(Settings::values.use_nvdec_emulation.UsingGlobal()); ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal()); ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal()); @@ -253,6 +261,8 @@ void ConfigureGraphics::SetupPerGameUI() { ConfigurationShared::SetColoredTristate( ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache); + ConfigurationShared::SetColoredTristate( + ui->use_nvdec_emulation, Settings::values.use_nvdec_emulation, use_nvdec_emulation); ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation, Settings::values.use_asynchronous_gpu_emulation, use_asynchronous_gpu_emulation); diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index b4961f719..1fefc88eb 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -46,6 +46,7 @@ private: std::unique_ptr<Ui::ConfigureGraphics> ui; QColor bg_color; + ConfigurationShared::CheckState use_nvdec_emulation; ConfigurationShared::CheckState use_disk_shader_cache; ConfigurationShared::CheckState use_asynchronous_gpu_emulation; diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 62aa337e7..58486eb1e 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -98,6 +98,13 @@ </widget> </item> <item> + <widget class="QCheckBox" name="use_nvdec_emulation"> + <property name="text"> + <string>Use NVDEC emulation</string> + </property> + </widget> + </item> + <item> <widget class="QWidget" name="aspect_ratio_layout" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_6"> <property name="leftMargin"> diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 73f276949..383c7bac8 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -32,7 +32,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { ui->gpu_accuracy->setCurrentIndex( static_cast<int>(Settings::values.gpu_accuracy.GetValue())); ui->anisotropic_filtering_combobox->setCurrentIndex( @@ -52,9 +52,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots) const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>( ui->gpu_accuracy->currentIndex() - - ((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); + ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { // Must guard in case of a during-game configuration when set to be game-specific. if (Settings::values.gpu_accuracy.UsingGlobal()) { Settings::values.gpu_accuracy.SetValue(gpu_accuracy); @@ -118,7 +118,7 @@ void ConfigureGraphicsAdvanced::RetranslateUI() { void ConfigureGraphicsAdvanced::SetupPerGameUI() { // Disable if not global (only happens during game) - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index f2977719c..d9009091b 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -8,18 +8,35 @@ #include <QSignalBlocker> #include <QTimer> -#include "configuration/configure_touchscreen_advanced.h" #include "core/core.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" -#include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/sm/sm.h" #include "ui_configure_input.h" +#include "ui_configure_input_advanced.h" #include "ui_configure_input_player.h" +#include "yuzu/configuration/configure_debug_controller.h" #include "yuzu/configuration/configure_input.h" +#include "yuzu/configuration/configure_input_advanced.h" #include "yuzu/configuration/configure_input_player.h" +#include "yuzu/configuration/configure_motion_touch.h" #include "yuzu/configuration/configure_mouse_advanced.h" +#include "yuzu/configuration/configure_touchscreen_advanced.h" +#include "yuzu/configuration/configure_vibration.h" +#include "yuzu/configuration/input_profiles.h" + +namespace { +template <typename Dialog, typename... Args> +void CallConfigureDialog(ConfigureInput& parent, Args&&... args) { + Dialog dialog(&parent, std::forward<Args>(args)...); + + const auto res = dialog.exec(); + if (res == QDialog::Accepted) { + dialog.ApplyConfiguration(); + } +} +} // Anonymous namespace void OnDockedModeChanged(bool last_state, bool new_state) { if (last_state == new_state) { @@ -48,97 +65,134 @@ void OnDockedModeChanged(bool last_state, bool new_state) { } } -namespace { -template <typename Dialog, typename... Args> -void CallConfigureDialog(ConfigureInput& parent, Args&&... args) { - parent.ApplyConfiguration(); - Dialog dialog(&parent, std::forward<Args>(args)...); - - const auto res = dialog.exec(); - if (res == QDialog::Accepted) { - dialog.ApplyConfiguration(); - } -} -} // Anonymous namespace - ConfigureInput::ConfigureInput(QWidget* parent) - : QDialog(parent), ui(std::make_unique<Ui::ConfigureInput>()) { + : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), + profiles(std::make_unique<InputProfiles>()) { ui->setupUi(this); +} + +ConfigureInput::~ConfigureInput() = default; - players_controller = { - ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox, - ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox, +void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, + std::size_t max_players) { + player_controllers = { + new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, + profiles.get()), }; - players_configure = { - ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure, - ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure, + player_tabs = { + ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, + ui->tabPlayer5, ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, }; - RetranslateUI(); - LoadConfiguration(); - UpdateUIEnabled(); + player_connected = { + ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected, + ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected, + ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, + }; - connect(ui->restore_defaults_button, &QPushButton::clicked, this, - &ConfigureInput::RestoreDefaults); + std::array<QLabel*, 8> player_connected_labels = { + ui->label, ui->label_3, ui->label_4, ui->label_5, + ui->label_6, ui->label_7, ui->label_8, ui->label_9, + }; - for (auto* enabled : players_controller) { - connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this, - &ConfigureInput::UpdateUIEnabled); - } - connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); - connect(ui->handheld_connected, &QCheckBox::stateChanged, this, - &ConfigureInput::UpdateUIEnabled); - connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); - connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); - connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); - connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this, - &ConfigureInput::UpdateUIEnabled); - - for (std::size_t i = 0; i < players_configure.size(); ++i) { - connect(players_configure[i], &QPushButton::clicked, this, - [this, i] { CallConfigureDialog<ConfigureInputPlayer>(*this, i, false); }); + for (std::size_t i = 0; i < player_tabs.size(); ++i) { + player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); + player_tabs[i]->layout()->addWidget(player_controllers[i]); + connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) { + if (is_connected) { + for (std::size_t index = 0; index <= i; ++index) { + player_connected[index]->setChecked(is_connected); + } + } else { + for (std::size_t index = i; index < player_tabs.size(); ++index) { + player_connected[index]->setChecked(is_connected); + } + } + }); + connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this, + &ConfigureInput::UpdateAllInputDevices); + connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputProfiles, this, + &ConfigureInput::UpdateAllInputProfiles, Qt::QueuedConnection); + connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) { + player_controllers[i]->ConnectPlayer(state == Qt::Checked); + }); + + // Remove/hide all the elements that exceed max_players, if applicable. + if (i >= max_players) { + ui->tabWidget->removeTab(static_cast<int>(max_players)); + player_connected[i]->hide(); + player_connected_labels[i]->hide(); + } } + // Only the first player can choose handheld mode so connect the signal just to player 1 + connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged, + [this](bool is_handheld) { UpdateDockedState(is_handheld); }); + + advanced = new ConfigureInputAdvanced(this); + ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); + ui->tabAdvanced->layout()->addWidget(advanced); + connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] { + CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem, profiles.get()); + }); + connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] { + CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem); + }); + connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog, + [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); + connect(advanced, &ConfigureInputAdvanced::CallMotionTouchConfigDialog, + [this, input_subsystem] { + CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); + }); - connect(ui->handheld_configure, &QPushButton::clicked, this, - [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 8, false); }); + connect(ui->vibrationButton, &QPushButton::clicked, + [this] { CallConfigureDialog<ConfigureVibration>(*this); }); - connect(ui->debug_configure, &QPushButton::clicked, this, - [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 9, true); }); + connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] { + CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); + }); - connect(ui->mouse_advanced, &QPushButton::clicked, this, - [this] { CallConfigureDialog<ConfigureMouseAdvanced>(*this); }); + connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); + connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); }); - connect(ui->touchscreen_advanced, &QPushButton::clicked, this, - [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); + RetranslateUI(); + LoadConfiguration(); } -ConfigureInput::~ConfigureInput() = default; +QList<QWidget*> ConfigureInput::GetSubTabs() const { + return { + ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5, + ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, ui->tabAdvanced, + }; +} void ConfigureInput::ApplyConfiguration() { - for (std::size_t i = 0; i < players_controller.size(); ++i) { - const auto controller_type_index = players_controller[i]->currentIndex(); + for (auto controller : player_controllers) { + controller->ApplyConfiguration(); + } - Settings::values.players[i].connected = controller_type_index != 0; + advanced->ApplyConfiguration(); - if (controller_type_index > 0) { - Settings::values.players[i].type = - static_cast<Settings::ControllerType>(controller_type_index - 1); - } else { - Settings::values.players[i].type = Settings::ControllerType::DualJoycon; - } - } + const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); + Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); + OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue()); - const bool pre_docked_mode = Settings::values.use_docked_mode; - Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); - OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); - Settings::values - .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)] - .connected = ui->handheld_connected->isChecked(); - Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked(); - Settings::values.mouse_enabled = ui->mouse_enabled->isChecked(); - Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked(); - Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); + Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); + Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); } void ConfigureInput::changeEvent(QEvent* event) { @@ -146,94 +200,74 @@ void ConfigureInput::changeEvent(QEvent* event) { RetranslateUI(); } - QDialog::changeEvent(event); + QWidget::changeEvent(event); } void ConfigureInput::RetranslateUI() { ui->retranslateUi(this); - RetranslateControllerComboBoxes(); } -void ConfigureInput::RetranslateControllerComboBoxes() { - for (auto* controller_box : players_controller) { - [[maybe_unused]] const QSignalBlocker blocker(controller_box); - - controller_box->clear(); - controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"), - tr("Single Right Joycon"), tr("Single Left Joycon")}); - } - +void ConfigureInput::LoadConfiguration() { LoadPlayerControllerIndices(); + UpdateDockedState(Settings::values.players.GetValue()[8].connected); + + ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); + ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); } -void ConfigureInput::UpdateUIEnabled() { - bool hit_disabled = false; - for (auto* player : players_controller) { - player->setDisabled(hit_disabled); - if (hit_disabled) { - player->setCurrentIndex(0); - } - if (!hit_disabled && player->currentIndex() == 0) { - hit_disabled = true; - } +void ConfigureInput::LoadPlayerControllerIndices() { + for (std::size_t i = 0; i < player_connected.size(); ++i) { + const auto connected = Settings::values.players.GetValue()[i].connected || + (i == 0 && Settings::values.players.GetValue()[8].connected); + player_connected[i]->setChecked(connected); } +} - for (std::size_t i = 0; i < players_controller.size(); ++i) { - players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0); - } +void ConfigureInput::ClearAll() { + // We don't have a good way to know what tab is active, but we can find out by getting the + // parent of the consoleInputSettings + auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent()); + player_tab->ClearAll(); +} - ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() && - !ui->use_docked_mode->isChecked()); - ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked()); - ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() && - !ui->use_docked_mode->isChecked()); - ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked()); - ui->debug_configure->setEnabled(ui->debug_enabled->isChecked()); - ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); +void ConfigureInput::RestoreDefaults() { + // We don't have a good way to know what tab is active, but we can find out by getting the + // parent of the consoleInputSettings + auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent()); + player_tab->RestoreDefaults(); + + ui->radioDocked->setChecked(true); + ui->radioUndocked->setChecked(false); + ui->vibrationGroup->setChecked(true); + ui->motionGroup->setChecked(true); } -void ConfigureInput::LoadConfiguration() { - std::stable_partition( - Settings::values.players.begin(), - Settings::values.players.begin() + - Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD), - [](const auto& player) { return player.connected; }); +void ConfigureInput::UpdateDockedState(bool is_handheld) { + // Disallow changing the console mode if the controller type is handheld. + ui->radioDocked->setEnabled(!is_handheld); + ui->radioUndocked->setEnabled(!is_handheld); - LoadPlayerControllerIndices(); + ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue()); + ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue()); - ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); - ui->handheld_connected->setChecked( - Settings::values - .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)] - .connected); - ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled); - ui->mouse_enabled->setChecked(Settings::values.mouse_enabled); - ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled); - ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); - - UpdateUIEnabled(); + // Also force into undocked mode if the controller type is handheld. + if (is_handheld) { + ui->radioUndocked->setChecked(true); + } } -void ConfigureInput::LoadPlayerControllerIndices() { - for (std::size_t i = 0; i < players_controller.size(); ++i) { - const auto connected = Settings::values.players[i].connected; - players_controller[i]->setCurrentIndex( - connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0); +void ConfigureInput::UpdateAllInputDevices() { + for (const auto& player : player_controllers) { + player->UpdateInputDeviceCombobox(); } } -void ConfigureInput::RestoreDefaults() { - players_controller[0]->setCurrentIndex(2); +void ConfigureInput::UpdateAllInputProfiles(std::size_t player_index) { + for (std::size_t i = 0; i < player_controllers.size(); ++i) { + if (i == player_index) { + continue; + } - for (std::size_t i = 1; i < players_controller.size(); ++i) { - players_controller[i]->setCurrentIndex(0); + player_controllers[i]->UpdateInputProfiles(); } - - ui->use_docked_mode->setCheckState(Qt::Unchecked); - ui->handheld_connected->setCheckState(Qt::Unchecked); - ui->mouse_enabled->setCheckState(Qt::Unchecked); - ui->keyboard_enabled->setCheckState(Qt::Unchecked); - ui->debug_enabled->setCheckState(Qt::Unchecked); - ui->touchscreen_enabled->setCheckState(Qt::Checked); - UpdateUIEnabled(); } diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index 2f70cb3ca..f4eb0d78b 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -7,37 +7,52 @@ #include <array> #include <memory> -#include <QDialog> #include <QKeyEvent> +#include <QList> +#include <QWidget> -#include "ui_configure_input.h" - -class QPushButton; +class QCheckBox; class QString; class QTimer; +class ConfigureInputAdvanced; +class ConfigureInputPlayer; + +class InputProfiles; + +namespace InputCommon { +class InputSubsystem; +} + namespace Ui { class ConfigureInput; } void OnDockedModeChanged(bool last_state, bool new_state); -class ConfigureInput : public QDialog { +class ConfigureInput : public QWidget { Q_OBJECT public: explicit ConfigureInput(QWidget* parent = nullptr); ~ConfigureInput() override; - /// Save all button configurations to settings file + /// Initializes the input dialog with the given input subsystem. + void Initialize(InputCommon::InputSubsystem* input_subsystem_, std::size_t max_players = 8); + + /// Save all button configurations to settings file. void ApplyConfiguration(); + QList<QWidget*> GetSubTabs() const; + private: void changeEvent(QEvent* event) override; void RetranslateUI(); - void RetranslateControllerComboBoxes(); + void ClearAll(); - void UpdateUIEnabled(); + void UpdateDockedState(bool is_handheld); + void UpdateAllInputDevices(); + void UpdateAllInputProfiles(std::size_t player_index); /// Load configuration settings. void LoadConfiguration(); @@ -48,6 +63,10 @@ private: std::unique_ptr<Ui::ConfigureInput> ui; - std::array<QComboBox*, 8> players_controller; - std::array<QPushButton*, 8> players_configure; + std::unique_ptr<InputProfiles> profiles; + + std::array<ConfigureInputPlayer*, 8> player_controllers; + std::array<QWidget*, 8> player_tabs; + std::array<QCheckBox*, 8> player_connected; + ConfigureInputAdvanced* advanced; }; diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui index efffd8487..2707025e7 100644 --- a/src/yuzu/configuration/configure_input.ui +++ b/src/yuzu/configuration/configure_input.ui @@ -1,529 +1,548 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>ConfigureInput</class> - <widget class="QDialog" name="ConfigureInput"> + <widget class="QWidget" name="ConfigureInput"> <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>384</width> - <height>576</height> + <width>680</width> + <height>540</height> </rect> </property> <property name="windowTitle"> - <string>Custom Input Settings</string> + <string>ConfigureInput</string> </property> <layout class="QVBoxLayout" name="verticalLayout_5"> + <property name="spacing"> + <number>2</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QGroupBox" name="gridGroupBox_1"> - <property name="title"> - <string>Players</string> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="1" column="2"> - <widget class="QComboBox" name="player1_combobox"> - <property name="minimumSize"> - <size> - <width>110</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="1" column="3"> - <widget class="QPushButton" name="player1_configure"> - <property name="text"> - <string>Configure</string> - </property> - </widget> - </item> - <item row="0" column="2"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Controller Type</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> - <item row="2" column="2"> - <widget class="QComboBox" name="player2_combobox"> - <property name="minimumSize"> - <size> - <width>110</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="3" column="2"> - <widget class="QComboBox" name="player3_combobox"> - <property name="minimumSize"> - <size> - <width>110</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="4" column="2"> - <widget class="QComboBox" name="player4_combobox"> - <property name="minimumSize"> - <size> - <width>110</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="5" column="2"> - <widget class="QComboBox" name="player5_combobox"> - <property name="minimumSize"> - <size> - <width>110</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="6" column="2"> - <widget class="QComboBox" name="player6_combobox"> - <property name="minimumSize"> - <size> - <width>110</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="7" column="2"> - <widget class="QComboBox" name="player7_combobox"> - <property name="minimumSize"> - <size> - <width>110</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="8" column="2"> - <widget class="QComboBox" name="player8_combobox"> - <property name="minimumSize"> - <size> - <width>110</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="2" column="3"> - <widget class="QPushButton" name="player2_configure"> - <property name="text"> - <string>Configure</string> - </property> - </widget> - </item> - <item row="3" column="3"> - <widget class="QPushButton" name="player3_configure"> - <property name="text"> - <string>Configure</string> - </property> - </widget> - </item> - <item row="4" column="3"> - <widget class="QPushButton" name="player4_configure"> - <property name="text"> - <string>Configure</string> - </property> - </widget> - </item> - <item row="5" column="3"> - <widget class="QPushButton" name="player5_configure"> - <property name="text"> - <string>Configure</string> - </property> - </widget> - </item> - <item row="6" column="3"> - <widget class="QPushButton" name="player6_configure"> - <property name="text"> - <string>Configure</string> - </property> - </widget> - </item> - <item row="7" column="3"> - <widget class="QPushButton" name="player7_configure"> - <property name="text"> - <string>Configure</string> - </property> - </widget> - </item> - <item row="8" column="3"> - <widget class="QPushButton" name="player8_configure"> - <property name="text"> - <string>Configure</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <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 row="0" column="4"> - <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> - <item row="1" column="1"> - <widget class="QLabel" name="label_3"> - <property name="minimumSize"> - <size> - <width>55</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Player 1</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string>Player 2</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QLabel" name="label_5"> - <property name="text"> - <string>Player 3</string> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="QLabel" name="label_6"> - <property name="text"> - <string>Player 4</string> - </property> - </widget> - </item> - <item row="5" column="1"> - <widget class="QLabel" name="label_7"> - <property name="text"> - <string>Player 5</string> - </property> - </widget> - </item> - <item row="6" column="1"> - <widget class="QLabel" name="label_8"> - <property name="text"> - <string>Player 6</string> - </property> - </widget> - </item> - <item row="7" column="1"> - <widget class="QLabel" name="label_9"> - <property name="text"> - <string>Player 7</string> - </property> - </widget> - </item> - <item row="8" column="1"> - <widget class="QLabel" name="label_10"> - <property name="text"> - <string>Player 8</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="gridGroupBox_2"> - <property name="title"> - <string>Handheld</string> - </property> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="1" column="2"> - <spacer name="horizontalSpacer_5"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>72</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="1" column="4"> - <spacer name="horizontalSpacer_4"> - <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 row="1" column="3"> - <widget class="QPushButton" name="handheld_configure"> - <property name="text"> - <string>Configure</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <spacer name="horizontalSpacer_3"> - <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 row="1" column="1"> - <widget class="QCheckBox" name="handheld_connected"> - <property name="text"> - <string>Joycons Docked</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QCheckBox" name="use_docked_mode"> - <property name="text"> - <string>Use Docked Mode</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="gridGroupBox_3"> - <property name="title"> - <string>Other</string> - </property> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="1" column="1"> - <widget class="QCheckBox" name="keyboard_enabled"> - <property name="minimumSize"> - <size> - <width>0</width> - <height>23</height> - </size> - </property> - <property name="text"> - <string>Keyboard</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QCheckBox" name="debug_enabled"> - <property name="text"> - <string>Debug Controller</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QCheckBox" name="touchscreen_enabled"> - <property name="text"> - <string>Touchscreen</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QCheckBox" name="mouse_enabled"> - <property name="minimumSize"> - <size> - <width>0</width> - <height>23</height> - </size> - </property> - <property name="text"> - <string>Mouse</string> - </property> - </widget> - </item> - <item row="0" column="4"> - <spacer name="horizontalSpacer_7"> - <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 row="0" column="2"> - <spacer name="horizontalSpacer_8"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>76</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="0" column="0"> - <spacer name="horizontalSpacer_6"> - <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 row="3" column="3"> - <widget class="QPushButton" name="touchscreen_advanced"> - <property name="text"> - <string>Advanced</string> - </property> - </widget> - </item> - <item row="2" column="3"> - <widget class="QPushButton" name="debug_configure"> - <property name="text"> - <string>Configure</string> - </property> - </widget> - </item> - <item row="0" column="3"> - <widget class="QPushButton" name="mouse_advanced"> - <property name="text"> - <string>Advanced</string> - </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>40</height> - </size> - </property> - </spacer> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QPushButton" name="restore_defaults_button"> - <property name="text"> - <string>Restore Defaults</string> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tabPlayer1"> + <property name="accessibleName"> + <string>Player 1</string> + </property> + <attribute name="title"> + <string>Player 1</string> + </attribute> + </widget> + <widget class="QWidget" name="tabPlayer2"> + <property name="accessibleName"> + <string>Player 2</string> + </property> + <attribute name="title"> + <string>Player 2</string> + </attribute> + </widget> + <widget class="QWidget" name="tabPlayer3"> + <property name="accessibleName"> + <string>Player 3</string> + </property> + <attribute name="title"> + <string>Player 3</string> + </attribute> + </widget> + <widget class="QWidget" name="tabPlayer4"> + <property name="accessibleName"> + <string>Player 4</string> + </property> + <attribute name="title"> + <string>Player 4</string> + </attribute> + </widget> + <widget class="QWidget" name="tabPlayer5"> + <property name="accessibleName"> + <string>Player 5</string> + </property> + <attribute name="title"> + <string>Player 5</string> + </attribute> + </widget> + <widget class="QWidget" name="tabPlayer6"> + <property name="accessibleName"> + <string>Player 6</string> + </property> + <attribute name="title"> + <string>Player 6</string> + </attribute> + </widget> + <widget class="QWidget" name="tabPlayer7"> + <property name="accessibleName"> + <string>Player 7</string> + </property> + <attribute name="title"> + <string>Player 7</string> + </attribute> + </widget> + <widget class="QWidget" name="tabPlayer8"> + <property name="accessibleName"> + <string>Player 8</string> + </property> + <attribute name="title"> + <string>Player 8</string> + </attribute> + </widget> + <widget class="QWidget" name="tabAdvanced"> + <property name="accessibleName"> + <string>Advanced</string> + </property> + <attribute name="title"> + <string>Advanced</string> + </attribute> + </widget> + </widget> + </item> + <item alignment="Qt::AlignVCenter"> + <widget class="QWidget" name="consoleInputSettings" native="true"> + <layout class="QHBoxLayout" name="buttonsBottomRightHorizontalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item alignment="Qt::AlignVCenter"> + <widget class="QGroupBox" name="handheldGroup"> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>16777215</height> + </size> + </property> + <property name="title"> + <string>Console Mode</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>8</number> + </property> + <property name="topMargin"> + <number>6</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>6</number> + </property> + <item> + <widget class="QRadioButton" name="radioDocked"> + <property name="text"> + <string>Docked</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="radioUndocked"> + <property name="text"> + <string>Undocked</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroup"> + <property name="title"> + <string>Vibration</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="vibrationButton"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Configure</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="motionGroup"> + <property name="title"> + <string>Motion</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="motionButton"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Configure</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignVCenter"> + <widget class="QWidget" name="connectedControllers" native="true"> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="leftMargin"> + <number>5</number> + </property> + <property name="topMargin"> + <number>0</number> </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_9"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <property name="rightMargin"> + <number>0</number> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> + <property name="bottomMargin"> + <number>0</number> </property> - </spacer> - </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + <property name="spacing"> + <number>3</number> </property> - </widget> - </item> - </layout> - </item> - </layout> + <item row="1" column="2"> + <widget class="QCheckBox" name="checkboxPlayer2Connected"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Controllers</string> + </property> + </widget> + </item> + <item row="1" column="4"> + <widget class="QCheckBox" name="checkboxPlayer4Connected"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QCheckBox" name="checkboxPlayer3Connected"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="1" column="5"> + <widget class="QCheckBox" name="checkboxPlayer5Connected"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>1</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="1" column="7"> + <widget class="QCheckBox" name="checkboxPlayer7Connected"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="1" column="6"> + <widget class="QCheckBox" name="checkboxPlayer6Connected"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QCheckBox" name="checkboxPlayer1Connected"> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="8"> + <widget class="QCheckBox" name="checkboxPlayer8Connected"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>2</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>3</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="0" column="4"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>4</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="0" column="5"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>5</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="0" column="6"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>6</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="0" column="7"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>7</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="0" column="8"> + <widget class="QLabel" name="label_9"> + <property name="text"> + <string>8</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_10"> + <property name="text"> + <string>Connected</string> + </property> + </widget> + </item> + </layout> + </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 alignment="Qt::AlignBottom"> + <widget class="QPushButton" name="buttonRestoreDefaults"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="sizeIncrement"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Defaults</string> + </property> + </widget> + </item> + <item alignment="Qt::AlignBottom"> + <widget class="QPushButton" name="buttonClearAll"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="sizeIncrement"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + </layout> + </widget> </item> </layout> </widget> <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>ConfigureInput</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>294</x> - <y>553</y> - </hint> - <hint type="destinationlabel"> - <x>191</x> - <y>287</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>ConfigureInput</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>294</x> - <y>553</y> - </hint> - <hint type="destinationlabel"> - <x>191</x> - <y>287</y> - </hint> - </hints> - </connection> - </connections> + <connections/> </ui> diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp new file mode 100644 index 000000000..abaf03630 --- /dev/null +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -0,0 +1,171 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QColorDialog> +#include "core/core.h" +#include "core/settings.h" +#include "ui_configure_input_advanced.h" +#include "yuzu/configuration/configure_input_advanced.h" + +ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) + : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) { + ui->setupUi(this); + + controllers_color_buttons = {{ + { + ui->player1_left_body_button, + ui->player1_left_buttons_button, + ui->player1_right_body_button, + ui->player1_right_buttons_button, + }, + { + ui->player2_left_body_button, + ui->player2_left_buttons_button, + ui->player2_right_body_button, + ui->player2_right_buttons_button, + }, + { + ui->player3_left_body_button, + ui->player3_left_buttons_button, + ui->player3_right_body_button, + ui->player3_right_buttons_button, + }, + { + ui->player4_left_body_button, + ui->player4_left_buttons_button, + ui->player4_right_body_button, + ui->player4_right_buttons_button, + }, + { + ui->player5_left_body_button, + ui->player5_left_buttons_button, + ui->player5_right_body_button, + ui->player5_right_buttons_button, + }, + { + ui->player6_left_body_button, + ui->player6_left_buttons_button, + ui->player6_right_body_button, + ui->player6_right_buttons_button, + }, + { + ui->player7_left_body_button, + ui->player7_left_buttons_button, + ui->player7_right_body_button, + ui->player7_right_buttons_button, + }, + { + ui->player8_left_body_button, + ui->player8_left_buttons_button, + ui->player8_right_body_button, + ui->player8_right_buttons_button, + }, + }}; + + for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) { + auto& color_buttons = controllers_color_buttons[player_idx]; + for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_idx) { + connect(color_buttons[button_idx], &QPushButton::clicked, this, + [this, player_idx, button_idx] { + OnControllerButtonClick(player_idx, button_idx); + }); + } + } + + connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, + &ConfigureInputAdvanced::UpdateUIEnabled); + connect(ui->debug_enabled, &QCheckBox::stateChanged, this, + &ConfigureInputAdvanced::UpdateUIEnabled); + connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this, + &ConfigureInputAdvanced::UpdateUIEnabled); + + connect(ui->debug_configure, &QPushButton::clicked, this, + [this] { CallDebugControllerDialog(); }); + connect(ui->mouse_advanced, &QPushButton::clicked, this, [this] { CallMouseConfigDialog(); }); + connect(ui->touchscreen_advanced, &QPushButton::clicked, this, + [this] { CallTouchscreenConfigDialog(); }); + connect(ui->buttonMotionTouch, &QPushButton::clicked, this, + &ConfigureInputAdvanced::CallMotionTouchConfigDialog); + + LoadConfiguration(); +} + +ConfigureInputAdvanced::~ConfigureInputAdvanced() = default; + +void ConfigureInputAdvanced::OnControllerButtonClick(std::size_t player_idx, + std::size_t button_idx) { + const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]); + if (!new_bg_color.isValid()) { + return; + } + controllers_colors[player_idx][button_idx] = new_bg_color; + controllers_color_buttons[player_idx][button_idx]->setStyleSheet( + QStringLiteral("background-color: %1; min-width: 60px;") + .arg(controllers_colors[player_idx][button_idx].name())); +} + +void ConfigureInputAdvanced::ApplyConfiguration() { + for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) { + auto& player = Settings::values.players.GetValue()[player_idx]; + std::array<u32, 4> colors{}; + std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(), + colors.begin(), [](QColor color) { return color.rgb(); }); + + player.body_color_left = colors[0]; + player.button_color_left = colors[1]; + player.body_color_right = colors[2]; + player.button_color_right = colors[3]; + } + + Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked(); + Settings::values.mouse_enabled = ui->mouse_enabled->isChecked(); + Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked(); + Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); +} + +void ConfigureInputAdvanced::LoadConfiguration() { + for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) { + auto& player = Settings::values.players.GetValue()[player_idx]; + std::array<u32, 4> colors = { + player.body_color_left, + player.button_color_left, + player.body_color_right, + player.button_color_right, + }; + + std::transform(colors.begin(), colors.end(), controllers_colors[player_idx].begin(), + [](u32 rgb) { return QColor::fromRgb(rgb); }); + + for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) { + controllers_color_buttons[player_idx][button_idx]->setStyleSheet( + QStringLiteral("background-color: %1; min-width: 60px;") + .arg(controllers_colors[player_idx][button_idx].name())); + } + } + + ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled); + ui->mouse_enabled->setChecked(Settings::values.mouse_enabled); + ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled); + ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); + + UpdateUIEnabled(); +} + +void ConfigureInputAdvanced::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QWidget::changeEvent(event); +} + +void ConfigureInputAdvanced::RetranslateUI() { + ui->retranslateUi(this); +} + +void ConfigureInputAdvanced::UpdateUIEnabled() { + ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked()); + ui->debug_configure->setEnabled(ui->debug_enabled->isChecked()); + ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); +} diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h new file mode 100644 index 000000000..3083d55c1 --- /dev/null +++ b/src/yuzu/configuration/configure_input_advanced.h @@ -0,0 +1,46 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <memory> +#include <QWidget> + +class QColor; +class QPushButton; + +namespace Ui { +class ConfigureInputAdvanced; +} + +class ConfigureInputAdvanced : public QWidget { + Q_OBJECT + +public: + explicit ConfigureInputAdvanced(QWidget* parent = nullptr); + ~ConfigureInputAdvanced() override; + + void ApplyConfiguration(); + +signals: + void CallDebugControllerDialog(); + void CallMouseConfigDialog(); + void CallTouchscreenConfigDialog(); + void CallMotionTouchConfigDialog(); + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + void UpdateUIEnabled(); + + void OnControllerButtonClick(std::size_t player_idx, std::size_t button_idx); + + void LoadConfiguration(); + + std::unique_ptr<Ui::ConfigureInputAdvanced> ui; + + std::array<std::array<QColor, 4>, 8> controllers_colors; + std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons; +}; diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui new file mode 100644 index 000000000..a880a7c68 --- /dev/null +++ b/src/yuzu/configuration/configure_input_advanced.ui @@ -0,0 +1,2688 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureInputAdvanced</class> + <widget class="QWidget" name="ConfigureInputAdvanced"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>710</width> + <height>580</height> + </rect> + </property> + <property name="windowTitle"> + <string>Configure Input</string> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>0</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="QWidget" name="mainInputAdvanced" native="true"> + <layout class="QHBoxLayout" name="main" stretch="1,1"> + <property name="spacing"> + <number>9</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="QWidget" name="leftInputAdvanced" native="true"> + <layout class="QVBoxLayout" name="leftLayout" stretch="0"> + <property name="spacing"> + <number>3</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="QGroupBox" name="joyconColorsGroup"> + <property name="title"> + <string>Joycon Colors</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,1"> + <property name="leftMargin"> + <number>9</number> + </property> + <property name="topMargin"> + <number>9</number> + </property> + <property name="rightMargin"> + <number>9</number> + </property> + <property name="bottomMargin"> + <number>9</number> + </property> + <item> + <widget class="QWidget" name="topLeftInputAdvanced" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <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="QWidget" name="player12Widget" 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="QGroupBox" name="player1Group"> + <property name="title"> + <string>Player 1</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>6</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>6</number> + </property> + <property name="bottomMargin"> + <number>6</number> + </property> + <item> + <widget class="QWidget" name="player1LeftJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_14"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player1LeftBodyGroup"> + <property name="title"> + <string>L Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_66"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player1_left_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player1LeftButtonsGroup"> + <property name="title"> + <string>L Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_67"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player1_left_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player1RightJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_14"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player1RightBodyGroup"> + <property name="title"> + <string>R Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_64"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player1_right_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player1RightButtonsGroup"> + <property name="title"> + <string>R Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_65"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player1_right_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="player2Group"> + <property name="title"> + <string>Player 2</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_14"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>6</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>6</number> + </property> + <property name="bottomMargin"> + <number>6</number> + </property> + <item> + <widget class="QWidget" name="player2LeftJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_15"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player2LeftBodyGroup"> + <property name="title"> + <string>L Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_70"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player2_left_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player2LeftButtonsGroup"> + <property name="title"> + <string>L Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_71"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player2_left_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player2RightJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_15"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player2RightBodyGroup"> + <property name="title"> + <string>R Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_68"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player2_right_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player2RightButtonsGroup"> + <property name="title"> + <string>R Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_69"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player2_right_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player34Widget" 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="QGroupBox" name="player3Group"> + <property name="title"> + <string>Player 3</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_15"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>6</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>6</number> + </property> + <property name="bottomMargin"> + <number>6</number> + </property> + <item> + <widget class="QWidget" name="player3LeftJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_16"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player3LeftBodyGroup"> + <property name="title"> + <string>L Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_74"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player3_left_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player3LeftButtonsGroup"> + <property name="title"> + <string>L Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_75"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player3_left_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player3RightJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_16"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player3RightBodyGroup"> + <property name="title"> + <string>R Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_72"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player3_right_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player3RightButtonsGroup"> + <property name="title"> + <string>R Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_73"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player3_right_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="player4Group"> + <property name="title"> + <string>Player 4</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_16"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>6</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>6</number> + </property> + <property name="bottomMargin"> + <number>6</number> + </property> + <item> + <widget class="QWidget" name="player4LeftJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_17"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player4LeftBodyGroup"> + <property name="title"> + <string>L Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_78"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player4_left_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player4LeftButtonsGroup"> + <property name="title"> + <string>L Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_79"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player4_left_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player4RightJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_17"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player4RightBodyGroup"> + <property name="title"> + <string>R Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_76"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player4_right_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player4RightButtonsGroup"> + <property name="title"> + <string>R Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_77"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player4_right_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="bottomLeftInputAdvanced" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <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="QWidget" name="player56Widget" 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="QGroupBox" name="player5Group"> + <property name="title"> + <string>Player 5</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_10"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>6</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>6</number> + </property> + <property name="bottomMargin"> + <number>6</number> + </property> + <item> + <widget class="QWidget" name="player5LeftJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_10"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player5LeftBodyGroup"> + <property name="title"> + <string>L Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_50"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player5_left_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player5LeftButtonsGroup"> + <property name="title"> + <string>L Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_51"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player5_left_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player5RightJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_10"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player5RightBodyGroup"> + <property name="title"> + <string>R Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_48"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player5_right_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player5RightButtonsGroup"> + <property name="title"> + <string>R Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_49"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player5_right_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="player6Group"> + <property name="title"> + <string>Player 6</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_11"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>6</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>6</number> + </property> + <property name="bottomMargin"> + <number>6</number> + </property> + <item> + <widget class="QWidget" name="player6LeftJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_11"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player6LeftBodyGroup"> + <property name="title"> + <string>L Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_54"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player6_left_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player6LeftButtonsGroup"> + <property name="title"> + <string>L Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_55"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player6_left_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player6RightJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_11"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player6RightBodyGroup"> + <property name="title"> + <string>R Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_52"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player6_right_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player6RightButtonsGroup"> + <property name="title"> + <string>R Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_53"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player6_right_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player78Widget" 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="QGroupBox" name="player7Group"> + <property name="title"> + <string>Player 7</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_12"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>6</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>6</number> + </property> + <property name="bottomMargin"> + <number>6</number> + </property> + <item> + <widget class="QWidget" name="player7LeftJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_12"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player7LeftBodyGroup"> + <property name="title"> + <string>L Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_58"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player7_left_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player7LeftButtonsGroup"> + <property name="title"> + <string>L Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_59"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player7_left_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player7RightJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_12"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player7RightBodyGroup"> + <property name="title"> + <string>R Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_56"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player7_right_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player7RightButtonsGroup"> + <property name="title"> + <string>R Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_57"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player7_right_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="player8Group"> + <property name="title"> + <string>Player 8</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_13"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>6</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>6</number> + </property> + <property name="bottomMargin"> + <number>6</number> + </property> + <item> + <widget class="QWidget" name="player8LeftJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_13"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player8LeftBodyGroup"> + <property name="title"> + <string>L Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_62"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player8_left_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player8LeftButtonsGroup"> + <property name="title"> + <string>L Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_63"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player8_left_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player8RightJoycon" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_13"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player8RightBodyGroup"> + <property name="title"> + <string>R Body</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_60"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player8_right_body_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="player8RightButtonsGroup"> + <property name="title"> + <string>R Button</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_61"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="player8_right_buttons_button"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="rightInputAdvanced" native="true"> + <layout class="QVBoxLayout" name="rightLayout" stretch="1,1"> + <property name="spacing"> + <number>3</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="QWidget" name="topRightInputAdvanced" native="true"> + <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> + <item> + <widget class="QGroupBox" name="gridGroupBox_3"> + <property name="title"> + <string>Other</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QCheckBox" name="keyboard_enabled"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>23</height> + </size> + </property> + <property name="text"> + <string>Keyboard</string> + </property> + </widget> + </item> + <item row="4" column="2"> + <widget class="QPushButton" name="touchscreen_advanced"> + <property name="text"> + <string>Advanced</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <spacer name="horizontalSpacer_8"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>76</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="mouse_advanced"> + <property name="text"> + <string>Advanced</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="touchscreen_enabled"> + <property name="text"> + <string>Touchscreen</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="mouse_enabled"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>23</height> + </size> + </property> + <property name="text"> + <string>Mouse</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="motion_touch"> + <property name="text"> + <string>Motion / Touch</string> + </property> + </widget> + </item> + <item row="6" column="2"> + <widget class="QPushButton" name="buttonMotionTouch"> + <property name="text"> + <string>Configure</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QCheckBox" name="debug_enabled"> + <property name="text"> + <string>Debug Controller</string> + </property> + </widget> + </item> + <item row="5" column="2"> + <widget class="QPushButton" name="debug_configure"> + <property name="text"> + <string>Configure</string> + </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>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="bottomRightInputAdvanced" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <spacer name="mainVerticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources> + </resources> + <connections/> +</ui> diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 597defe8c..56ab32a35 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -4,19 +4,28 @@ #include <algorithm> #include <memory> +#include <thread> #include <utility> -#include <QColorDialog> #include <QGridLayout> +#include <QInputDialog> #include <QKeyEvent> #include <QMenu> #include <QMessageBox> #include <QTimer> -#include "common/assert.h" #include "common/param_package.h" +#include "core/core.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/hid.h" +#include "core/hle/service/sm/sm.h" +#include "input_common/gcadapter/gc_poller.h" #include "input_common/main.h" +#include "input_common/udp/udp.h" #include "ui_configure_input_player.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" +#include "yuzu/configuration/configure_vibration.h" +#include "yuzu/configuration/input_profiles.h" +#include "yuzu/util/limitable_input_dialog.h" const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> ConfigureInputPlayer::analog_sub_buttons{{ @@ -24,20 +33,74 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> "down", "left", "right", - "modifier", }}; -static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) { - const int index1 = grid->indexOf(item); - const int index2 = grid->indexOf(onTopOf); - int row, column, rowSpan, columnSpan; - grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan); - grid->takeAt(index1); - grid->addWidget(item, row, column, rowSpan, columnSpan); +namespace { + +constexpr std::size_t HANDHELD_INDEX = 8; + +void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, + bool connected) { + Core::System& system{Core::System::GetInstance()}; + if (!system.IsPoweredOn()) { + return; + } + Service::SM::ServiceManager& sm = system.ServiceManager(); + + auto& npad = + sm.GetService<Service::HID::Hid>("hid") + ->GetAppletResource() + ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad); + + npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected); +} + +/// Maps the controller type combobox index to Controller Type enum +constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) { + switch (index) { + case 0: + default: + return Settings::ControllerType::ProController; + case 1: + return Settings::ControllerType::DualJoyconDetached; + case 2: + return Settings::ControllerType::LeftJoycon; + case 3: + return Settings::ControllerType::RightJoycon; + case 4: + return Settings::ControllerType::Handheld; + } +} + +/// Maps the Controller Type enum to controller type combobox index +constexpr int GetIndexFromControllerType(Settings::ControllerType type) { + switch (type) { + case Settings::ControllerType::ProController: + default: + return 0; + case Settings::ControllerType::DualJoyconDetached: + return 1; + case Settings::ControllerType::LeftJoycon: + return 2; + case Settings::ControllerType::RightJoycon: + return 3; + case Settings::ControllerType::Handheld: + return 4; + } } -static QString GetKeyName(int key_code) { +QString GetKeyName(int key_code) { switch (key_code) { + case Qt::LeftButton: + return QObject::tr("Click 0"); + case Qt::RightButton: + return QObject::tr("Click 1"); + case Qt::MiddleButton: + return QObject::tr("Click 2"); + case Qt::BackButton: + return QObject::tr("Click 3"); + case Qt::ForwardButton: + return QObject::tr("Click 4"); case Qt::Key_Shift: return QObject::tr("Shift"); case Qt::Key_Control: @@ -51,9 +114,16 @@ static QString GetKeyName(int key_code) { } } -static void SetAnalogButton(const Common::ParamPackage& input_param, - Common::ParamPackage& analog_param, const std::string& button_name) { - if (analog_param.Get("engine", "") != "analog_from_button") { +void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param, + const std::string& button_name) { + // The poller returned a complete axis, so set all the buttons + if (input_param.Has("axis_x") && input_param.Has("axis_y")) { + analog_param = input_param; + return; + } + // Check if the current configuration has either no engine or an axis binding. + // Clears out the old binding and adds one with analog_from_button. + if (!analog_param.Has("engine") || analog_param.Has("axis_x") || analog_param.Has("axis_y")) { analog_param = { {"engine", "analog_from_button"}, }; @@ -61,7 +131,7 @@ static void SetAnalogButton(const Common::ParamPackage& input_param, analog_param.Set(button_name, input_param.Serialize()); } -static QString ButtonToText(const Common::ParamPackage& param) { +QString ButtonToText(const Common::ParamPackage& param) { if (!param.Has("engine")) { return QObject::tr("[not set]"); } @@ -84,6 +154,14 @@ static QString ButtonToText(const Common::ParamPackage& param) { return GetKeyName(param.Get("code", 0)); } + if (param.Get("engine", "") == "cemuhookudp") { + if (param.Has("pad_index")) { + const QString motion_str = QString::fromStdString(param.Get("pad_index", "")); + return QObject::tr("Motion %1").arg(motion_str); + } + return GetKeyName(param.Get("code", 0)); + } + if (param.Get("engine", "") == "sdl") { if (param.Has("hat")) { const QString hat_str = QString::fromStdString(param.Get("hat", "")); @@ -111,7 +189,7 @@ static QString ButtonToText(const Common::ParamPackage& param) { return QObject::tr("[unknown]"); } -static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { +QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { if (!param.Has("engine")) { return QObject::tr("[not set]"); } @@ -161,22 +239,26 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string } return QObject::tr("[unknown]"); } - -ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug) - : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), - debug(debug), timeout_timer(std::make_unique<QTimer>()), - poll_timer(std::make_unique<QTimer>()) { +} // namespace + +ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, + QWidget* bottom_row, + InputCommon::InputSubsystem* input_subsystem_, + InputProfiles* profiles_, bool debug) + : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), + debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_), + timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()), + bottom_row(bottom_row) { ui->setupUi(this); + setFocusPolicy(Qt::ClickFocus); button_map = { - ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, - ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, - ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, - ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, - ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown, - ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown, - ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot, + ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, + ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, + ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, + ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, + ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot, }; analog_map_buttons = {{ @@ -185,122 +267,67 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i ui->buttonLStickDown, ui->buttonLStickLeft, ui->buttonLStickRight, - ui->buttonLStickMod, }, { ui->buttonRStickUp, ui->buttonRStickDown, ui->buttonRStickLeft, ui->buttonRStickRight, - ui->buttonRStickMod, }, }}; - debug_hidden = { - ui->buttonSL, ui->labelSL, - ui->buttonSR, ui->labelSR, - ui->buttonLStick, ui->labelLStickPressed, - ui->buttonRStick, ui->labelRStickPressed, - ui->buttonHome, ui->labelHome, - ui->buttonScreenshot, ui->labelScreenshot, + motion_map = { + ui->buttonMotionLeft, + ui->buttonMotionRight, }; - auto layout = Settings::values.players[player_index].type; - if (debug) - layout = Settings::ControllerType::DualJoycon; - - switch (layout) { - case Settings::ControllerType::ProController: - case Settings::ControllerType::DualJoycon: - layout_hidden = { - ui->buttonSL, - ui->labelSL, - ui->buttonSR, - ui->labelSR, - }; - break; - case Settings::ControllerType::LeftJoycon: - layout_hidden = { - ui->right_body_button, - ui->right_buttons_button, - ui->right_body_label, - ui->right_buttons_label, - ui->buttonR, - ui->labelR, - ui->buttonZR, - ui->labelZR, - ui->labelHome, - ui->buttonHome, - ui->buttonPlus, - ui->labelPlus, - ui->RStick, - ui->faceButtons, - }; - break; - case Settings::ControllerType::RightJoycon: - layout_hidden = { - ui->left_body_button, ui->left_buttons_button, - ui->left_body_label, ui->left_buttons_label, - ui->buttonL, ui->labelL, - ui->buttonZL, ui->labelZL, - ui->labelScreenshot, ui->buttonScreenshot, - ui->buttonMinus, ui->labelMinus, - ui->LStick, ui->Dpad, - }; - break; - } - - if (debug || layout == Settings::ControllerType::ProController) { - ui->controller_color->hide(); - } else { - if (layout == Settings::ControllerType::LeftJoycon || - layout == Settings::ControllerType::RightJoycon) { - ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0}); - - LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad); - LayerGridElements(ui->buttons, ui->misc, ui->RStick); - LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons); - LayerGridElements(ui->buttons, ui->RStick, ui->LStick); - } - } - - for (auto* widget : layout_hidden) - widget->setVisible(false); - - analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; - analog_map_deadzone_and_modifier_slider = {ui->sliderLStickDeadzoneAndModifier, - ui->sliderRStickDeadzoneAndModifier}; - analog_map_deadzone_and_modifier_slider_label = {ui->labelLStickDeadzoneAndModifier, - ui->labelRStickDeadzoneAndModifier}; - - for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { - auto* const button = button_map[button_id]; - if (button == nullptr) { - continue; - } - - button->setContextMenuPolicy(Qt::CustomContextMenu); + analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone}; + analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone}; + analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup}; + analog_map_modifier_button = {ui->buttonLStickMod, ui->buttonRStickMod}; + analog_map_modifier_label = {ui->labelLStickModifierRange, ui->labelRStickModifierRange}; + analog_map_modifier_slider = {ui->sliderLStickModifierRange, ui->sliderRStickModifierRange}; + analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup}; + analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange}; + + const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param, + int default_val, InputCommon::Polling::DeviceType type) { connect(button, &QPushButton::clicked, [=, this] { HandleClick( - button_map[button_id], + button, [=, this](Common::ParamPackage params) { - // Workaround for ZL & ZR for analog triggers like on XBOX controllors. - // Analog triggers (from controllers like the XBOX controller) would not - // work due to a different range of their signals (from 0 to 255 on - // analog triggers instead of -32768 to 32768 on analog joysticks). The - // SDL driver misinterprets analog triggers as analog joysticks. + // Workaround for ZL & ZR for analog triggers like on XBOX + // controllers. Analog triggers (from controllers like the XBOX + // controller) would not work due to a different range of their + // signals (from 0 to 255 on analog triggers instead of -32768 to + // 32768 on analog joysticks). The SDL driver misinterprets analog + // triggers as analog joysticks. // TODO: reinterpret the signal range for analog triggers to map the - // values correctly. This is required for the correct emulation of the - // analog triggers of the GameCube controller. - if (button_id == Settings::NativeButton::ZL || - button_id == Settings::NativeButton::ZR) { + // values correctly. This is required for the correct emulation of + // the analog triggers of the GameCube controller. + if (button == ui->buttonZL || button == ui->buttonZR) { params.Set("direction", "+"); params.Set("threshold", "0.5"); } - buttons_param[button_id] = std::move(params); + *param = std::move(params); }, - InputCommon::Polling::DeviceType::Button); + type); }); + }; + + for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { + auto* const button = button_map[button_id]; + + if (button == nullptr) { + continue; + } + + ConfigureButtonClick(button_map[button_id], &buttons_param[button_id], + Config::default_buttons[button_id], + InputCommon::Polling::DeviceType::Button); + + button->setContextMenuPolicy(Qt::CustomContextMenu); + connect(button, &QPushButton::customContextMenuRequested, [=, this](const QPoint& menu_location) { QMenu context_menu; @@ -308,100 +335,198 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i buttons_param[button_id].Clear(); button_map[button_id]->setText(tr("[not set]")); }); - context_menu.addAction(tr("Restore Default"), [&] { - buttons_param[button_id] = Common::ParamPackage{ - InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; - button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); - }); context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); }); } - for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { - for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { + for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { + auto* const button = motion_map[motion_id]; + if (button == nullptr) { + continue; + } + + ConfigureButtonClick(motion_map[motion_id], &motions_param[motion_id], + Config::default_motions[motion_id], + InputCommon::Polling::DeviceType::Motion); + + button->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(button, &QPushButton::customContextMenuRequested, + [=, this](const QPoint& menu_location) { + QMenu context_menu; + context_menu.addAction(tr("Clear"), [&] { + motions_param[motion_id].Clear(); + motion_map[motion_id]->setText(tr("[not set]")); + }); + context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location)); + }); + } + + for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { + for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; + if (analog_button == nullptr) { continue; } - analog_button->setContextMenuPolicy(Qt::CustomContextMenu); connect(analog_button, &QPushButton::clicked, [=, this] { + if (!map_analog_stick_accepted) { + map_analog_stick_accepted = + QMessageBox::information( + this, tr("Map Analog Stick"), + tr("After pressing OK, first move your joystick horizontally, and then " + "vertically.\nTo invert the axes, first move your joystick " + "vertically, and then horizontally."), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok; + if (!map_analog_stick_accepted) { + return; + } + } HandleClick( analog_map_buttons[analog_id][sub_button_id], [=, this](const Common::ParamPackage& params) { - SetAnalogButton(params, analogs_param[analog_id], - analog_sub_buttons[sub_button_id]); + SetAnalogParam(params, analogs_param[analog_id], + analog_sub_buttons[sub_button_id]); }, - InputCommon::Polling::DeviceType::Button); + InputCommon::Polling::DeviceType::AnalogPreferred); }); + + analog_button->setContextMenuPolicy(Qt::CustomContextMenu); + connect(analog_button, &QPushButton::customContextMenuRequested, [=, this](const QPoint& menu_location) { QMenu context_menu; context_menu.addAction(tr("Clear"), [&] { - analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); + analogs_param[analog_id].Clear(); analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); }); - context_menu.addAction(tr("Restore Default"), [&] { - Common::ParamPackage params{InputCommon::GenerateKeyboardParam( - Config::default_analogs[analog_id][sub_button_id])}; - SetAnalogButton(params, analogs_param[analog_id], - analog_sub_buttons[sub_button_id]); - analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText( - analogs_param[analog_id], analog_sub_buttons[sub_button_id])); - }); context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal( menu_location)); }); } - connect(analog_map_stick[analog_id], &QPushButton::clicked, [=, this] { - if (QMessageBox::information( - this, tr("Information"), - tr("After pressing OK, first move your joystick horizontally, " - "and then vertically."), - QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { - HandleClick( - analog_map_stick[analog_id], - [=, this](const Common::ParamPackage& params) { - analogs_param[analog_id] = params; - }, - InputCommon::Polling::DeviceType::Analog); - } + + // Handle clicks for the modifier buttons as well. + connect(analog_map_modifier_button[analog_id], &QPushButton::clicked, [=, this] { + HandleClick( + analog_map_modifier_button[analog_id], + [=, this](const Common::ParamPackage& params) { + analogs_param[analog_id].Set("modifier", params.Serialize()); + }, + InputCommon::Polling::DeviceType::Button); }); - connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, + analog_map_modifier_button[analog_id]->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested, + [=, this](const QPoint& menu_location) { + QMenu context_menu; + context_menu.addAction(tr("Clear"), [&] { + analogs_param[analog_id].Set("modifier", ""); + analog_map_modifier_button[analog_id]->setText(tr("[not set]")); + }); + context_menu.exec( + analog_map_modifier_button[analog_id]->mapToGlobal(menu_location)); + }); + + connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged), [=, this] { - const float slider_value = - analog_map_deadzone_and_modifier_slider[analog_id]->value(); - if (analogs_param[analog_id].Get("engine", "") == "sdl" || - analogs_param[analog_id].Get("engine", "") == "gcpad") { - analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( - tr("Deadzone: %1%").arg(slider_value)); - analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); - } else { - analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( - tr("Modifier Scale: %1%").arg(slider_value)); - analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); - } + const auto spinbox_value = analog_map_range_spinbox[analog_id]->value(); + analogs_param[analog_id].Set("range", spinbox_value / 100.0f); + }); + + connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] { + const auto slider_value = analog_map_deadzone_slider[analog_id]->value(); + analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value)); + analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); + }); + + connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] { + const auto slider_value = analog_map_modifier_slider[analog_id]->value(); + analog_map_modifier_label[analog_id]->setText( + tr("Modifier Range: %1%").arg(slider_value)); + analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); + }); + } + + // Player Connected checkbox + connect(ui->groupConnectedController, &QGroupBox::toggled, + [this](bool checked) { emit Connected(checked); }); + + // Set up controller type. Only Player 1 can choose Handheld. + ui->comboControllerType->clear(); + + QStringList controller_types = { + tr("Pro Controller"), + tr("Dual Joycons"), + tr("Left Joycon"), + tr("Right Joycon"), + }; + + if (player_index == 0) { + controller_types.append(tr("Handheld")); + connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), + [this](int index) { + emit HandheldStateChanged(GetControllerTypeFromIndex(index) == + Settings::ControllerType::Handheld); }); } - connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); - connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); }); + if (debug || player_index == 9) { + ui->groupConnectedController->setCheckable(false); + } + + // The Debug Controller can only choose the Pro Controller. + if (debug) { + ui->buttonScreenshot->setEnabled(false); + ui->buttonHome->setEnabled(false); + QStringList debug_controller_types = { + tr("Pro Controller"), + }; + ui->comboControllerType->addItems(debug_controller_types); + } else { + ui->comboControllerType->addItems(controller_types); + } + + UpdateControllerIcon(); + UpdateControllerAvailableButtons(); + UpdateMotionButtons(); + connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) { + UpdateControllerIcon(); + UpdateControllerAvailableButtons(); + UpdateMotionButtons(); + }); + + connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this, + &ConfigureInputPlayer::UpdateMappingWithDefaults); + + ui->comboDevices->setCurrentIndex(-1); + + ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); + connect(ui->buttonRefreshDevices, &QPushButton::clicked, + [this] { emit RefreshInputDevices(); }); timeout_timer->setSingleShot(true); connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); connect(poll_timer.get(), &QTimer::timeout, [this] { Common::ParamPackage params; - if (InputCommon::GetGCButtons()->IsPolling()) { - params = InputCommon::GetGCButtons()->GetNextInput(); - if (params.Has("engine")) { + if (input_subsystem->GetGCButtons()->IsPolling()) { + params = input_subsystem->GetGCButtons()->GetNextInput(); + if (params.Has("engine") && IsInputAcceptable(params)) { SetPollingResult(params, false); return; } } - if (InputCommon::GetGCAnalogs()->IsPolling()) { - params = InputCommon::GetGCAnalogs()->GetNextInput(); + if (input_subsystem->GetGCAnalogs()->IsPolling()) { + params = input_subsystem->GetGCAnalogs()->GetNextInput(); + if (params.Has("engine") && IsInputAcceptable(params)) { + SetPollingResult(params, false); + return; + } + } + if (input_subsystem->GetUDPMotions()->IsPolling()) { + params = input_subsystem->GetUDPMotions()->GetNextInput(); if (params.Has("engine")) { SetPollingResult(params, false); return; @@ -409,27 +534,25 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } for (auto& poller : device_pollers) { params = poller->GetNextInput(); - if (params.Has("engine")) { + if (params.Has("engine") && IsInputAcceptable(params)) { SetPollingResult(params, false); return; } } }); - controller_color_buttons = { - ui->left_body_button, - ui->left_buttons_button, - ui->right_body_button, - ui->right_buttons_button, - }; + UpdateInputProfiles(); - for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) { - connect(controller_color_buttons[i], &QPushButton::clicked, this, - [this, i] { OnControllerButtonClick(static_cast<int>(i)); }); - } + connect(ui->buttonProfilesNew, &QPushButton::clicked, this, + &ConfigureInputPlayer::CreateProfile); + connect(ui->buttonProfilesDelete, &QPushButton::clicked, this, + &ConfigureInputPlayer::DeleteProfile); + connect(ui->comboProfiles, qOverload<int>(&QComboBox::activated), this, + &ConfigureInputPlayer::LoadProfile); + connect(ui->buttonProfilesSave, &QPushButton::clicked, this, + &ConfigureInputPlayer::SaveProfile); LoadConfiguration(); - resize(0, 0); // TODO(wwylele): enable this when we actually emulate it ui->buttonHome->setEnabled(false); @@ -438,27 +561,72 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i ConfigureInputPlayer::~ConfigureInputPlayer() = default; void ConfigureInputPlayer::ApplyConfiguration() { - auto& buttons = - debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons; - auto& analogs = - debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs; + auto& player = Settings::values.players.GetValue()[player_index]; + auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons; + auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs; std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(), [](const Common::ParamPackage& param) { return param.Serialize(); }); std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(), [](const Common::ParamPackage& param) { return param.Serialize(); }); - if (debug) + if (debug) { + return; + } + + auto& motions = player.motions; + + std::transform(motions_param.begin(), motions_param.end(), motions.begin(), + [](const Common::ParamPackage& param) { return param.Serialize(); }); + + const auto controller_type = + GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); + const auto player_connected = ui->groupConnectedController->isChecked() && + controller_type != Settings::ControllerType::Handheld; + + if (player.controller_type == controller_type && player.connected == player_connected) { + // Set vibration devices in the event that the input device has changed. + ConfigureVibration::SetVibrationDevices(player_index); return; + } + + // Disconnect the controller first. + UpdateController(controller_type, player_index, false); + + player.controller_type = controller_type; + player.connected = player_connected; + + ConfigureVibration::SetVibrationDevices(player_index); + + // Handheld + if (player_index == 0) { + auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; + if (controller_type == Settings::ControllerType::Handheld) { + handheld = player; + } + handheld.connected = ui->groupConnectedController->isChecked() && + controller_type == Settings::ControllerType::Handheld; + UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); + } + + if (!player.connected) { + return; + } - std::array<u32, 4> colors{}; - std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(), - [](QColor color) { return color.rgb(); }); + // This emulates a delay between disconnecting and reconnecting controllers as some games + // do not respond to a change in controller type if it was instantaneous. + using namespace std::chrono_literals; + std::this_thread::sleep_for(20ms); - Settings::values.players[player_index].body_color_left = colors[0]; - Settings::values.players[player_index].button_color_left = colors[1]; - Settings::values.players[player_index].body_color_right = colors[2]; - Settings::values.players[player_index].button_color_right = colors[3]; + UpdateController(controller_type, player_index, player_connected); +} + +void ConfigureInputPlayer::showEvent(QShowEvent* event) { + if (bottom_row == nullptr) { + return; + } + QWidget::showEvent(event); + ui->main->addWidget(bottom_row); } void ConfigureInputPlayer::changeEvent(QEvent* event) { @@ -466,24 +634,16 @@ void ConfigureInputPlayer::changeEvent(QEvent* event) { RetranslateUI(); } - QDialog::changeEvent(event); + QWidget::changeEvent(event); } void ConfigureInputPlayer::RetranslateUI() { ui->retranslateUi(this); - UpdateButtonLabels(); -} - -void ConfigureInputPlayer::OnControllerButtonClick(int i) { - const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]); - if (!new_bg_color.isValid()) - return; - controller_colors[i] = new_bg_color; - controller_color_buttons[i]->setStyleSheet( - QStringLiteral("QPushButton { background-color: %1 }").arg(controller_colors[i].name())); + UpdateUI(); } void ConfigureInputPlayer::LoadConfiguration() { + auto& player = Settings::values.players.GetValue()[player_index]; if (debug) { std::transform(Settings::values.debug_pad_buttons.begin(), Settings::values.debug_pad_buttons.end(), buttons_param.begin(), @@ -492,68 +652,109 @@ void ConfigureInputPlayer::LoadConfiguration() { Settings::values.debug_pad_analogs.end(), analogs_param.begin(), [](const std::string& str) { return Common::ParamPackage(str); }); } else { - std::transform(Settings::values.players[player_index].buttons.begin(), - Settings::values.players[player_index].buttons.end(), buttons_param.begin(), + std::transform(player.buttons.begin(), player.buttons.end(), buttons_param.begin(), + [](const std::string& str) { return Common::ParamPackage(str); }); + std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(), [](const std::string& str) { return Common::ParamPackage(str); }); - std::transform(Settings::values.players[player_index].analogs.begin(), - Settings::values.players[player_index].analogs.end(), analogs_param.begin(), + std::transform(player.motions.begin(), player.motions.end(), motions_param.begin(), [](const std::string& str) { return Common::ParamPackage(str); }); } - UpdateButtonLabels(); + UpdateUI(); + UpdateInputDeviceCombobox(); - if (debug) + if (debug) { return; + } - std::array<u32, 4> colors = { - Settings::values.players[player_index].body_color_left, - Settings::values.players[player_index].button_color_left, - Settings::values.players[player_index].body_color_right, - Settings::values.players[player_index].button_color_right, - }; + ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type)); + ui->groupConnectedController->setChecked( + player.connected || + (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected)); +} - std::transform(colors.begin(), colors.end(), controller_colors.begin(), - [](u32 rgb) { return QColor::fromRgb(rgb); }); +void ConfigureInputPlayer::ConnectPlayer(bool connected) { + ui->groupConnectedController->setChecked(connected); +} - for (std::size_t i = 0; i < colors.size(); ++i) { - controller_color_buttons[i]->setStyleSheet( - QStringLiteral("QPushButton { background-color: %1 }") - .arg(controller_colors[i].name())); +void ConfigureInputPlayer::UpdateInputDeviceCombobox() { + // Skip input device persistence if "Input Devices" is set to "Any". + if (ui->comboDevices->currentIndex() == 0) { + UpdateInputDevices(); + return; } -} -void ConfigureInputPlayer::RestoreDefaults() { - for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { - buttons_param[button_id] = Common::ParamPackage{ - InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; + // Find the first button that isn't empty. + const auto button_param = + std::find_if(buttons_param.begin(), buttons_param.end(), + [](const Common::ParamPackage param) { return param.Has("engine"); }); + const bool buttons_empty = button_param == buttons_param.end(); + + const auto current_engine = button_param->Get("engine", ""); + const auto current_guid = button_param->Get("guid", ""); + const auto current_port = button_param->Get("port", ""); + + const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse"; + + UpdateInputDevices(); + + if (buttons_empty) { + return; } - for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { - for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { - Common::ParamPackage params{InputCommon::GenerateKeyboardParam( - Config::default_analogs[analog_id][sub_button_id])}; - SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); + const bool all_one_device = + std::all_of(buttons_param.begin(), buttons_param.end(), + [current_engine, current_guid, current_port, + is_keyboard_mouse](const Common::ParamPackage param) { + if (is_keyboard_mouse) { + return !param.Has("engine") || param.Get("engine", "") == "keyboard" || + param.Get("engine", "") == "mouse"; + } + return !param.Has("engine") || (param.Get("engine", "") == current_engine && + param.Get("guid", "") == current_guid && + param.Get("port", "") == current_port); + }); + + if (all_one_device) { + if (is_keyboard_mouse) { + ui->comboDevices->setCurrentIndex(1); + return; } + const auto devices_it = std::find_if( + input_devices.begin(), input_devices.end(), + [current_engine, current_guid, current_port](const Common::ParamPackage param) { + return param.Get("class", "") == current_engine && + param.Get("guid", "") == current_guid && + param.Get("port", "") == current_port; + }); + const int device_index = + devices_it != input_devices.end() + ? static_cast<int>(std::distance(input_devices.begin(), devices_it)) + : 0; + ui->comboDevices->setCurrentIndex(device_index); + } else { + ui->comboDevices->setCurrentIndex(0); } +} - UpdateButtonLabels(); - ApplyConfiguration(); +void ConfigureInputPlayer::RestoreDefaults() { + UpdateMappingWithDefaults(); } void ConfigureInputPlayer::ClearAll() { - for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { + for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { const auto* const button = button_map[button_id]; - if (button == nullptr || !button->isEnabled()) { + if (button == nullptr) { continue; } buttons_param[button_id].Clear(); } - for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { - for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { + for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { + for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; - if (analog_button == nullptr || !analog_button->isEnabled()) { + if (analog_button == nullptr) { continue; } @@ -561,17 +762,30 @@ void ConfigureInputPlayer::ClearAll() { } } - UpdateButtonLabels(); - ApplyConfiguration(); + for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { + const auto* const motion_button = motion_map[motion_id]; + if (motion_button == nullptr) { + continue; + } + + motions_param[motion_id].Clear(); + } + + UpdateUI(); + UpdateInputDevices(); } -void ConfigureInputPlayer::UpdateButtonLabels() { - for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { +void ConfigureInputPlayer::UpdateUI() { + for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) { button_map[button]->setText(ButtonToText(buttons_param[button])); } - for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { - for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { + for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { + motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id])); + } + + for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { + for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; if (analog_button == nullptr) { @@ -581,99 +795,329 @@ void ConfigureInputPlayer::UpdateButtonLabels() { analog_button->setText( AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); } - analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); + analog_map_modifier_button[analog_id]->setText( + ButtonToText(Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")})); + + const auto deadzone_label = analog_map_deadzone_label[analog_id]; + const auto deadzone_slider = analog_map_deadzone_slider[analog_id]; + const auto modifier_groupbox = analog_map_modifier_groupbox[analog_id]; + const auto modifier_label = analog_map_modifier_label[analog_id]; + const auto modifier_slider = analog_map_modifier_slider[analog_id]; + const auto range_groupbox = analog_map_range_groupbox[analog_id]; + const auto range_spinbox = analog_map_range_spinbox[analog_id]; + + int slider_value; auto& param = analogs_param[analog_id]; - auto* const analog_stick_slider = analog_map_deadzone_and_modifier_slider[analog_id]; - auto* const analog_stick_slider_label = - analog_map_deadzone_and_modifier_slider_label[analog_id]; - - if (param.Has("engine")) { - if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") { - if (!param.Has("deadzone")) { - param.Set("deadzone", 0.1f); - } + const bool is_controller = + param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad"; - analog_stick_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100)); - if (analog_stick_slider->value() == 0) { - analog_stick_slider_label->setText(tr("Deadzone: 0%")); - } - } else { - if (!param.Has("modifier_scale")) { - param.Set("modifier_scale", 0.5f); - } + if (is_controller) { + if (!param.Has("deadzone")) { + param.Set("deadzone", 0.1f); + } + slider_value = static_cast<int>(param.Get("deadzone", 0.1f) * 100); + deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value)); + deadzone_slider->setValue(slider_value); + if (!param.Has("range")) { + param.Set("range", 1.0f); + } + range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100)); + } else { + if (!param.Has("modifier_scale")) { + param.Set("modifier_scale", 0.5f); + } + slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100); + modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value)); + modifier_slider->setValue(slider_value); + } - analog_stick_slider->setValue( - static_cast<int>(param.Get("modifier_scale", 0.5f) * 100)); - if (analog_stick_slider->value() == 0) { - analog_stick_slider_label->setText(tr("Modifier Scale: 0%")); - } + deadzone_label->setVisible(is_controller); + deadzone_slider->setVisible(is_controller); + modifier_groupbox->setVisible(!is_controller); + modifier_label->setVisible(!is_controller); + modifier_slider->setVisible(!is_controller); + range_groupbox->setVisible(is_controller); + } +} + +void ConfigureInputPlayer::UpdateInputDevices() { + input_devices = input_subsystem->GetInputDevices(); + ui->comboDevices->clear(); + for (auto device : input_devices) { + ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {}); + } +} + +void ConfigureInputPlayer::UpdateControllerIcon() { + // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its + // "nonstandard" to use an image through the icon support) + const QString stylesheet = [this] { + switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { + case Settings::ControllerType::ProController: + return QStringLiteral("image: url(:/controller/pro_controller%0)"); + case Settings::ControllerType::DualJoyconDetached: + return QStringLiteral("image: url(:/controller/dual_joycon%0)"); + case Settings::ControllerType::LeftJoycon: + return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)"); + case Settings::ControllerType::RightJoycon: + return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)"); + case Settings::ControllerType::Handheld: + return QStringLiteral("image: url(:/controller/handheld%0)"); + default: + return QString{}; + } + }(); + + const QString theme = [] { + if (QIcon::themeName().contains(QStringLiteral("dark"))) { + return QStringLiteral("_dark"); + } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { + return QStringLiteral("_midnight"); + } else { + return QString{}; + } + }(); + + ui->controllerFrame->setStyleSheet(stylesheet.arg(theme)); +} + +void ConfigureInputPlayer::UpdateControllerAvailableButtons() { + auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); + if (debug) { + layout = Settings::ControllerType::ProController; + } + + // List of all the widgets that will be hidden by any of the following layouts that need + // "unhidden" after the controller type changes + const std::array<QWidget*, 9> layout_show = { + ui->buttonShoulderButtonsSLSR, + ui->horizontalSpacerShoulderButtonsWidget, + ui->horizontalSpacerShoulderButtonsWidget2, + ui->buttonShoulderButtonsLeft, + ui->buttonMiscButtonsMinusScreenshot, + ui->bottomLeft, + ui->buttonShoulderButtonsRight, + ui->buttonMiscButtonsPlusHome, + ui->bottomRight, + }; + + for (auto* widget : layout_show) { + widget->show(); + } + + std::vector<QWidget*> layout_hidden; + switch (layout) { + case Settings::ControllerType::ProController: + case Settings::ControllerType::DualJoyconDetached: + case Settings::ControllerType::Handheld: + layout_hidden = { + ui->buttonShoulderButtonsSLSR, + ui->horizontalSpacerShoulderButtonsWidget2, + }; + break; + case Settings::ControllerType::LeftJoycon: + layout_hidden = { + ui->horizontalSpacerShoulderButtonsWidget2, + ui->buttonShoulderButtonsRight, + ui->buttonMiscButtonsPlusHome, + ui->bottomRight, + }; + break; + case Settings::ControllerType::RightJoycon: + layout_hidden = { + ui->horizontalSpacerShoulderButtonsWidget, + ui->buttonShoulderButtonsLeft, + ui->buttonMiscButtonsMinusScreenshot, + ui->bottomLeft, + }; + break; + } + + for (auto* widget : layout_hidden) { + widget->hide(); + } +} + +void ConfigureInputPlayer::UpdateMotionButtons() { + if (debug) { + // Motion isn't used with the debug controller, hide both groupboxes. + ui->buttonMotionLeftGroup->hide(); + ui->buttonMotionRightGroup->hide(); + return; + } + + // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller. + switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { + case Settings::ControllerType::ProController: + case Settings::ControllerType::LeftJoycon: + case Settings::ControllerType::Handheld: + // Show "Motion 1" and hide "Motion 2". + ui->buttonMotionLeftGroup->show(); + ui->buttonMotionRightGroup->hide(); + break; + case Settings::ControllerType::RightJoycon: + // Show "Motion 2" and hide "Motion 1". + ui->buttonMotionLeftGroup->hide(); + ui->buttonMotionRightGroup->show(); + break; + case Settings::ControllerType::DualJoyconDetached: + default: + // Show both "Motion 1/2". + ui->buttonMotionLeftGroup->show(); + ui->buttonMotionRightGroup->show(); + break; + } +} + +void ConfigureInputPlayer::UpdateMappingWithDefaults() { + if (ui->comboDevices->currentIndex() == 0) { + return; + } + + if (ui->comboDevices->currentIndex() == 1) { + // Reset keyboard bindings + for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { + buttons_param[button_id] = Common::ParamPackage{ + InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; + } + for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { + for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { + Common::ParamPackage params{InputCommon::GenerateKeyboardParam( + Config::default_analogs[analog_id][sub_button_id])}; + SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); } + + analogs_param[analog_id].Set("modifier", InputCommon::GenerateKeyboardParam( + Config::default_stick_mod[analog_id])); + } + + for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { + motions_param[motion_id] = Common::ParamPackage{ + InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])}; } + + UpdateUI(); + return; + } + + // Reset controller bindings + const auto& device = input_devices[ui->comboDevices->currentIndex()]; + auto button_mapping = input_subsystem->GetButtonMappingForDevice(device); + auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device); + for (std::size_t i = 0; i < buttons_param.size(); ++i) { + buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)]; + } + for (std::size_t i = 0; i < analogs_param.size(); ++i) { + analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)]; } + + UpdateUI(); } void ConfigureInputPlayer::HandleClick( QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, InputCommon::Polling::DeviceType type) { - button->setText(tr("[press key]")); + if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { + button->setText(tr("Shake!")); + } else { + button->setText(tr("[waiting]")); + } button->setFocus(); - // Keyboard keys can only be used as button devices - want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; - if (want_keyboard_keys) { - const auto iter = std::find(button_map.begin(), button_map.end(), button); - ASSERT(iter != button_map.end()); - const auto index = std::distance(button_map.begin(), iter); - ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); - } + // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a + // controller, then they don't want keyboard/mouse input + want_keyboard_mouse = ui->comboDevices->currentIndex() < 2; input_setter = new_input_setter; - device_pollers = InputCommon::Polling::GetPollers(type); + device_pollers = input_subsystem->GetPollers(type); for (auto& poller : device_pollers) { poller->Start(); } - grabKeyboard(); - grabMouse(); + QWidget::grabMouse(); + QWidget::grabKeyboard(); + if (type == InputCommon::Polling::DeviceType::Button) { - InputCommon::GetGCButtons()->BeginConfiguration(); + input_subsystem->GetGCButtons()->BeginConfiguration(); } else { - InputCommon::GetGCAnalogs()->BeginConfiguration(); + input_subsystem->GetGCAnalogs()->BeginConfiguration(); } - timeout_timer->start(5000); // Cancel after 5 seconds - poll_timer->start(200); // Check for new inputs every 200ms + + if (type == InputCommon::Polling::DeviceType::Motion) { + input_subsystem->GetUDPMotions()->BeginConfiguration(); + } + + timeout_timer->start(2500); // Cancel after 2.5 seconds + poll_timer->start(50); // Check for new inputs every 50ms } void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) { - releaseKeyboard(); - releaseMouse(); timeout_timer->stop(); poll_timer->stop(); for (auto& poller : device_pollers) { poller->Stop(); } - InputCommon::GetGCButtons()->EndConfiguration(); - InputCommon::GetGCAnalogs()->EndConfiguration(); + QWidget::releaseMouse(); + QWidget::releaseKeyboard(); + + input_subsystem->GetGCButtons()->EndConfiguration(); + input_subsystem->GetGCAnalogs()->EndConfiguration(); + + input_subsystem->GetUDPMotions()->EndConfiguration(); if (!abort) { (*input_setter)(params); } - UpdateButtonLabels(); + UpdateUI(); + UpdateInputDeviceCombobox(); + input_setter = std::nullopt; } +bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) const { + if (ui->comboDevices->currentIndex() == 0) { + return true; + } + + // Keyboard/Mouse + if (ui->comboDevices->currentIndex() == 1) { + return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse"; + } + + const auto current_input_device = input_devices[ui->comboDevices->currentIndex()]; + return params.Get("engine", "") == current_input_device.Get("class", "") && + params.Get("guid", "") == current_input_device.Get("guid", "") && + params.Get("port", "") == current_input_device.Get("port", ""); +} + +void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) { + if (!input_setter || !event) { + return; + } + + if (want_keyboard_mouse) { + SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())}, + false); + } else { + // We don't want any mouse buttons, so don't stop polling + return; + } + + SetPollingResult({}, true); +} + void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { if (!input_setter || !event) { return; } if (event->key() != Qt::Key_Escape) { - if (want_keyboard_keys) { + if (want_keyboard_mouse) { SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, false); } else { @@ -681,5 +1125,105 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { return; } } + SetPollingResult({}, true); } + +void ConfigureInputPlayer::CreateProfile() { + const auto profile_name = + LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20); + + if (profile_name.isEmpty()) { + return; + } + + if (!InputProfiles::IsProfileNameValid(profile_name.toStdString())) { + QMessageBox::critical(this, tr("Create Input Profile"), + tr("The given profile name is not valid!")); + return; + } + + ApplyConfiguration(); + + if (!profiles->CreateProfile(profile_name.toStdString(), player_index)) { + QMessageBox::critical(this, tr("Create Input Profile"), + tr("Failed to create the input profile \"%1\"").arg(profile_name)); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); + return; + } + + emit RefreshInputProfiles(player_index); + + ui->comboProfiles->addItem(profile_name); + ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1); +} + +void ConfigureInputPlayer::DeleteProfile() { + const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); + + if (profile_name.isEmpty()) { + return; + } + + if (!profiles->DeleteProfile(profile_name.toStdString())) { + QMessageBox::critical(this, tr("Delete Input Profile"), + tr("Failed to delete the input profile \"%1\"").arg(profile_name)); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); + return; + } + + emit RefreshInputProfiles(player_index); + + ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex()); + ui->comboProfiles->setCurrentIndex(-1); +} + +void ConfigureInputPlayer::LoadProfile() { + const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); + + if (profile_name.isEmpty()) { + return; + } + + ApplyConfiguration(); + + if (!profiles->LoadProfile(profile_name.toStdString(), player_index)) { + QMessageBox::critical(this, tr("Load Input Profile"), + tr("Failed to load the input profile \"%1\"").arg(profile_name)); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); + return; + } + + LoadConfiguration(); +} + +void ConfigureInputPlayer::SaveProfile() { + const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); + + if (profile_name.isEmpty()) { + return; + } + + ApplyConfiguration(); + + if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) { + QMessageBox::critical(this, tr("Save Input Profile"), + tr("Failed to save the input profile \"%1\"").arg(profile_name)); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); + return; + } +} + +void ConfigureInputPlayer::UpdateInputProfiles() { + ui->comboProfiles->clear(); + + for (const auto& profile_name : profiles->GetInputProfileNames()) { + ui->comboProfiles->addItem(QString::fromStdString(profile_name)); + } + + ui->comboProfiles->setCurrentIndex(-1); +} diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 95afa5375..23cf6f958 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -10,16 +10,27 @@ #include <optional> #include <string> -#include <QDialog> +#include <QWidget> #include "common/param_package.h" #include "core/settings.h" #include "ui_configure_input.h" +class QCheckBox; class QKeyEvent; +class QLabel; class QPushButton; +class QSlider; +class QSpinBox; class QString; class QTimer; +class QWidget; + +class InputProfiles; + +namespace InputCommon { +class InputSubsystem; +} namespace InputCommon::Polling { class DevicePoller; @@ -30,86 +41,165 @@ namespace Ui { class ConfigureInputPlayer; } -class ConfigureInputPlayer : public QDialog { +class ConfigureInputPlayer : public QWidget { Q_OBJECT public: - explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug = false); + explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row, + InputCommon::InputSubsystem* input_subsystem_, + InputProfiles* profiles_, bool debug = false); ~ConfigureInputPlayer() override; - /// Save all button configurations to settings file + /// Save all button configurations to settings file. void ApplyConfiguration(); -private: - void changeEvent(QEvent* event) override; - void RetranslateUI(); + /// Set the connection state checkbox (used to sync state). + void ConnectPlayer(bool connected); - void OnControllerButtonClick(int i); + /// Update the input devices combobox. + void UpdateInputDeviceCombobox(); + + /// Updates the list of controller profiles. + void UpdateInputProfiles(); - /// Load configuration settings. - void LoadConfiguration(); /// Restore all buttons to their default values. void RestoreDefaults(); - /// Clear all input configuration + + /// Clear all input configuration. void ClearAll(); - /// Update UI to reflect current configuration. - void UpdateButtonLabels(); +signals: + /// Emitted when this controller is connected by the user. + void Connected(bool connected); + /// Emitted when the Handheld mode is selected (undocked with dual joycons attached). + void HandheldStateChanged(bool is_handheld); + /// Emitted when the input devices combobox is being refreshed. + void RefreshInputDevices(); + /** + * Emitted when the input profiles combobox is being refreshed. + * The player_index represents the current player's index, and the profile combobox + * will not be updated for this index as they are already updated by other mechanisms. + */ + void RefreshInputProfiles(std::size_t player_index); + +protected: + void showEvent(QShowEvent* event) override; + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + /// Load configuration settings. + void LoadConfiguration(); /// Called when the button was pressed. void HandleClick(QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, InputCommon::Polling::DeviceType type); - /// Finish polling and configure input using the input_setter + /// Finish polling and configure input using the input_setter. void SetPollingResult(const Common::ParamPackage& params, bool abort); + /// Checks whether a given input can be accepted. + bool IsInputAcceptable(const Common::ParamPackage& params) const; + + /// Handle mouse button press events. + void mousePressEvent(QMouseEvent* event) override; + /// Handle key press events. void keyPressEvent(QKeyEvent* event) override; + /// Update UI to reflect current configuration. + void UpdateUI(); + + /// Update the available input devices. + void UpdateInputDevices(); + + /// Update the current controller icon. + void UpdateControllerIcon(); + + /// Hides and disables controller settings based on the current controller type. + void UpdateControllerAvailableButtons(); + + /// Shows or hides motion groupboxes based on the current controller type. + void UpdateMotionButtons(); + + /// Gets the default controller mapping for this device and auto configures the input to match. + void UpdateMappingWithDefaults(); + + /// Creates a controller profile. + void CreateProfile(); + + /// Deletes the selected controller profile. + void DeleteProfile(); + + /// Loads the selected controller profile. + void LoadProfile(); + + /// Saves the current controller configuration into a selected controller profile. + void SaveProfile(); + std::unique_ptr<Ui::ConfigureInputPlayer> ui; std::size_t player_index; bool debug; + InputCommon::InputSubsystem* input_subsystem; + + InputProfiles* profiles; + std::unique_ptr<QTimer> timeout_timer; std::unique_ptr<QTimer> poll_timer; + static constexpr int PLAYER_COUNT = 8; + std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox; + /// This will be the the setting function when an input is awaiting configuration. std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; + std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions> motions_param; - static constexpr int ANALOG_SUB_BUTTONS_NUM = 5; + static constexpr int ANALOG_SUB_BUTTONS_NUM = 4; /// Each button input is represented by a QPushButton. std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; - std::vector<QWidget*> debug_hidden; - std::vector<QWidget*> layout_hidden; - - /// A group of five QPushButtons represent one analog input. The buttons each represent up, - /// down, left, right, and modifier, respectively. + /// A group of four QPushButtons represent one analog input. The buttons each represent up, + /// down, left, right, respectively. std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs> analog_map_buttons; - /// Analog inputs are also represented each with a single button, used to configure with an - /// actual analog stick - std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; - std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> - analog_map_deadzone_and_modifier_slider; - std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> - analog_map_deadzone_and_modifier_slider_label; + /// Each motion input is represented by a QPushButton. + std::array<QPushButton*, Settings::NativeMotion::NumMotions> motion_map; + + std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label; + std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_slider; + std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_groupbox; + std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_button; + std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_label; + std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_slider; + std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_groupbox; + std::array<QSpinBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_spinbox; static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; + /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once. + bool map_analog_stick_accepted{}; + /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, /// keyboard events are ignored. - bool want_keyboard_keys = false; + bool want_keyboard_mouse{}; + + /// List of physical devices users can map with. If a SDL backed device is selected, then you + /// can use this device to get a default mapping. + std::vector<Common::ParamPackage> input_devices; - std::array<QPushButton*, 4> controller_color_buttons; - std::array<QColor, 4> controller_colors; + /// Bottom row is where console wide settings are held, and its "owned" by the parent + /// ConfigureInput widget. On show, add this widget to the main layout. This will change the + /// parent of the widget to this widget (but thats fine). + QWidget* bottom_row; }; diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index f27a77180..1e78b4c10 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -1,1243 +1,3094 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>ConfigureInputPlayer</class> - <widget class="QDialog" name="ConfigureInputPlayer"> + <widget class="QWidget" name="ConfigureInputPlayer"> <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>408</width> - <height>731</height> + <width>780</width> + <height>487</height> </rect> </property> <property name="windowTitle"> <string>Configure Input</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_5"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="spacing"> + <number>0</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> - <layout class="QGridLayout" name="buttons"> - <item row="1" column="1"> - <widget class="QGroupBox" name="RStick"> - <property name="title"> - <string>Right Stick</string> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> - </property> - <property name="flat"> - <bool>false</bool> + <layout class="QVBoxLayout" name="main"> + <property name="spacing"> + <number>0</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> + <layout class="QHBoxLayout" name="top" stretch="0,1,2"> + <property name="spacing"> + <number>3</number> </property> - <property name="checkable"> - <bool>false</bool> + <property name="topMargin"> + <number>0</number> </property> - <layout class="QGridLayout" name="gridLayout_5"> - <item row="1" column="1"> - <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout"> + <item> + <widget class="QGroupBox" name="groupConnectedController"> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="title"> + <string>Connect Controller</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <property name="leftMargin"> + <number>5</number> + </property> + <property name="topMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>5</number> + </property> + <property name="bottomMargin"> + <number>5</number> + </property> <item> - <layout class="QHBoxLayout" name="buttonRStickDownHorizontalLayout"> + <widget class="QComboBox" name="comboControllerType"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>21</height> + </size> + </property> <item> - <widget class="QLabel" name="labelRStickDown"> - <property name="text"> - <string>Down:</string> - </property> - </widget> + <property name="text"> + <string>Pro Controller</string> + </property> </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonRStickDown"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="0" column="1"> - <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonRStickRightHorizontalLayout"> <item> - <widget class="QLabel" name="labelRStickRight"> - <property name="text"> - <string>Right:</string> - </property> - </widget> + <property name="text"> + <string>Dual Joycons</string> + </property> </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonRStickRight"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="3" column="0" colspan="2"> - <widget class="QPushButton" name="buttonRStickAnalog"> - <property name="text"> - <string>Set Analog Stick</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonRStickLeftHorizontalLayout"> <item> - <widget class="QLabel" name="labelRStickLeft"> - <property name="text"> - <string>Left:</string> - </property> - </widget> + <property name="text"> + <string>Left Joycon</string> + </property> </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonRStickLeft"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="1" column="0"> - <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonRStickUpHorizontalLayout"> <item> - <widget class="QLabel" name="labelRStickUp"> - <property name="text"> - <string>Up:</string> - </property> - </widget> + <property name="text"> + <string>Right Joycon</string> + </property> </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonRStickUp"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="2" column="0"> - <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonRStickPressedHorizontalLayout"> <item> - <widget class="QLabel" name="labelRStickPressed"> - <property name="text"> - <string>Pressed:</string> - </property> - </widget> + <property name="text"> + <string>Handheld</string> + </property> </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonRStick"> - <property name="text"> - <string/> - </property> </widget> </item> </layout> - </item> - <item row="2" column="1"> - <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout"> + </widget> + </item> + <item> + <widget class="QGroupBox" name="devicesGroup"> + <property name="title"> + <string>Input Device</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>5</number> + </property> + <property name="topMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>5</number> + </property> + <property name="bottomMargin"> + <number>5</number> + </property> <item> - <layout class="QHBoxLayout" name="buttonRStickModHorizontalLayout"> + <widget class="QComboBox" name="comboDevices"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>21</height> + </size> + </property> <item> - <widget class="QLabel" name="labelRStickMod"> - <property name="text"> - <string>Modifier:</string> - </property> - </widget> + <property name="text"> + <string>Any</string> + </property> </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonRStickMod"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="4" column="0" colspan="2"> - <layout class="QVBoxLayout" name="sliderRStickDeadzoneAndModifierVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="sliderRStickDeadzoneAndModifierHorizontalLayout"> <item> - <widget class="QLabel" name="labelRStickDeadzoneAndModifier"> - <property name="text"> - <string>Deadzone: 0</string> - </property> - <property name="alignment"> - <enum>Qt::AlignHCenter</enum> - </property> - </widget> + <property name="text"> + <string>Keyboard/Mouse</string> + </property> </item> - </layout> + </widget> </item> <item> - <widget class="QSlider" name="sliderRStickDeadzoneAndModifier"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <widget class="QPushButton" name="buttonRefreshDevices"> + <property name="minimumSize"> + <size> + <width>21</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>21</width> + <height>21</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true"/> </property> </widget> </item> </layout> - </item> - <item row="5" column="0"> - <spacer name="RStick_verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> + </widget> + </item> + <item> + <widget class="QGroupBox" name="profilesGroup"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="title"> + <string>Profile</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="2,0,0,0"> + <property name="spacing"> + <number>3</number> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>0</width> - <height>0</height> - </size> + <property name="leftMargin"> + <number>5</number> + </property> + <property name="topMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>5</number> + </property> + <property name="bottomMargin"> + <number>5</number> </property> - </spacer> - </item> - </layout> - </widget> - </item> - <item row="0" column="1"> - <widget class="QGroupBox" name="Dpad"> - <property name="title"> - <string>Directional Pad</string> - </property> - <property name="flat"> - <bool>false</bool> - </property> - <property name="checkable"> - <bool>false</bool> - </property> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="1" column="0"> - <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonDpadUpHorizontalLayout"> - <item> - <widget class="QLabel" name="labelDpadUp"> - <property name="text"> - <string>Up:</string> - </property> - </widget> - </item> - </layout> - </item> <item> - <widget class="QPushButton" name="buttonDpadUp"> - <property name="text"> - <string/> + <widget class="QComboBox" name="comboProfiles"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>21</height> + </size> </property> </widget> </item> - </layout> - </item> - <item row="1" column="1"> - <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonDpadDownHorizontalLayout"> - <item> - <widget class="QLabel" name="labelDpadDown"> - <property name="text"> - <string>Down:</string> - </property> - </widget> - </item> - </layout> - </item> <item> - <widget class="QPushButton" name="buttonDpadDown"> + <widget class="QPushButton" name="buttonProfilesSave"> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> <property name="text"> - <string/> + <string>Save</string> </property> </widget> </item> - </layout> - </item> - <item row="0" column="0"> - <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonDpadLeftHorizontalLayout"> - <item> - <widget class="QLabel" name="labelDpadLeft"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Left:</string> - </property> - </widget> - </item> - </layout> - </item> <item> - <widget class="QPushButton" name="buttonDpadLeft"> + <widget class="QPushButton" name="buttonProfilesNew"> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> <property name="text"> - <string/> + <string>New</string> </property> </widget> </item> - </layout> - </item> - <item row="0" column="1"> - <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonDpadRightHorizontalLayout"> - <item> - <widget class="QLabel" name="labelDpadRight"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Right:</string> - </property> - </widget> - </item> - </layout> - </item> <item> - <widget class="QPushButton" name="buttonDpadRight"> + <widget class="QPushButton" name="buttonProfilesDelete"> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> <property name="text"> - <string/> + <string>Delete</string> </property> </widget> </item> </layout> - </item> - </layout> - </widget> + </widget> + </item> + </layout> </item> - <item row="0" column="0"> - <widget class="QGroupBox" name="faceButtons"> - <property name="title"> - <string>Face Buttons</string> - </property> - <property name="flat"> - <bool>false</bool> - </property> - <property name="checkable"> - <bool>false</bool> + <item> + <widget class="QFrame" name="bottom"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonFaceButtonsAHorizontalLayout"> - <item> - <widget class="QLabel" name="labelA"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> + <layout class="QHBoxLayout" name="_2"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</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> + <widget class="QWidget" name="bottomLeft" native="true"> + <layout class="QVBoxLayout" name="bottomLeftLayout" stretch="0,0,0,0"> + <property name="spacing"> + <number>0</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> + <widget class="QGroupBox" name="LStick"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Left Stick</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="spacing"> + <number>0</number> </property> - <property name="text"> - <string>A:</string> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonA"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="0" column="1"> - <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonFaceButtonsBHorizontalLayout"> - <item> - <widget class="QLabel" name="labelB"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> + <property name="leftMargin"> + <number>3</number> </property> - <property name="text"> - <string>B:</string> + <property name="topMargin"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonB"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="1" column="0"> - <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonFaceButtonsXHorizontalLayout"> - <item> - <widget class="QLabel" name="labelX"> - <property name="text"> - <string>X:</string> + <property name="rightMargin"> + <number>3</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonX"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="1" column="1"> - <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonFaceButtonsYHorizontalLayout"> - <item> - <widget class="QLabel" name="labelY"> - <property name="text"> - <string>Y:</string> + <property name="bottomMargin"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonY"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item row="5" column="0" colspan="2"> - <widget class="QGroupBox" name="controller_color"> - <property name="title"> - <string>Controller Color</string> - </property> - <layout class="QGridLayout" name="gridLayout_10" columnstretch="0,0,0,0,0,0,0"> - <item row="0" column="0"> - <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> - <item row="0" column="1"> - <widget class="QLabel" name="left_body_label"> - <property name="text"> - <string>Left Body</string> - </property> - </widget> - </item> - <item row="0" column="6"> - <spacer name="horizontalSpacer_3"> - <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 row="1" column="1"> - <widget class="QLabel" name="left_buttons_label"> - <property name="minimumSize"> - <size> - <width>90</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Left Buttons</string> - </property> - </widget> - </item> - <item row="1" column="5"> - <widget class="QPushButton" name="right_buttons_button"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>32</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>40</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="0" column="4"> - <widget class="QLabel" name="right_body_label"> - <property name="text"> - <string>Right Body</string> - </property> - </widget> - </item> - <item row="1" column="4"> - <widget class="QLabel" name="right_buttons_label"> - <property name="minimumSize"> - <size> - <width>90</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Right Buttons</string> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="QPushButton" name="left_buttons_button"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>32</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>40</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="0" column="2"> - <widget class="QPushButton" name="left_body_button"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>32</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>40</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="0" column="5"> - <widget class="QPushButton" name="right_body_button"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>32</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>40</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="0" column="3"> - <spacer name="horizontalSpacer_4"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - </item> - <item row="1" column="0"> - <widget class="QGroupBox" name="LStick"> - <property name="title"> - <string>Left Stick</string> - </property> - <property name="flat"> - <bool>false</bool> - </property> - <property name="checkable"> - <bool>false</bool> - </property> - <layout class="QGridLayout" name="gridLayout_4"> - <item row="1" column="1"> - <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonLStickUpHorizontalLayout"> - <item> - <widget class="QLabel" name="labelLStickUp"> - <property name="text"> - <string>Up:</string> + <item> + <widget class="QWidget" name="buttonLStickUpWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_20"> + <property name="spacing"> + <number>0</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> + <spacer name="horizontalSpacerLStickUpLeft"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonLStickUpGroup"> + <property name="title"> + <string>Up</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonLStickUp"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Up</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="horizontalSpacerLStickUpRight"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="buttonLStickLeftRightHorizontaLayout"> + <property name="spacing"> + <number>3</number> + </property> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonLStickLeftGroup"> + <property name="title"> + <string>Left</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonLStickLeft"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Left</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonLStickRightGroup"> + <property name="title"> + <string>Right</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonLStickRight"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Right</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QWidget" name="buttonLStickDownWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_22"> + <property name="spacing"> + <number>0</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> + <spacer name="horizontalSpacerLStickDownLeft"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonLStickDownGroup"> + <property name="title"> + <string>Down</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonLStickDown"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Down</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="horizontalSpacerLStickDownRight"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="buttonLStickPressedModifierHorizontalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonLStickPressedGroup"> + <property name="title"> + <string>Pressed</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonLStick"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Pressed</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonLStickModGroup"> + <property name="title"> + <string>Modifier</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonLStickMod"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Modifier</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="buttonLStickRangeGroup"> + <property name="title"> + <string>Range</string> + </property> + <layout class="QHBoxLayout" name="buttonLStickRangeGroupHorizontalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="spinboxLStickRange"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>50</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="sliderLStickDeadzoneModifierRangeVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>2</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout"> + <item> + <widget class="QLabel" name="labelLStickDeadzone"> + <property name="text"> + <string>Deadzone: 0%</string> + </property> + <property name="alignment"> + <set>Qt::AlignHCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QSlider" name="sliderLStickDeadzone"> + <property name="maximum"> + <number>100</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="sliderLStickModifierRangeHorizontalLayout"> + <item> + <widget class="QLabel" name="labelLStickModifierRange"> + <property name="text"> + <string>Modifier Range: 0%</string> + </property> + <property name="alignment"> + <set>Qt::AlignHCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QSlider" name="sliderLStickModifierRange"> + <property name="maximum"> + <number>100</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacerBottomLeft"> + <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="QGroupBox" name="Dpad"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>D-Pad</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <property name="spacing"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonLStickUp"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="0" column="2"> - <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonLStickRightHorizontalLayout"> - <item> - <widget class="QLabel" name="labelLStickRight"> - <property name="text"> - <string>Right:</string> + <property name="leftMargin"> + <number>3</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonLStickRight"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="4" column="1" colspan="2"> - <widget class="QPushButton" name="buttonLStickAnalog"> - <property name="text"> - <string>Set Analog Stick</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonLStickLeftHorizontalLayout_2"> - <item> - <widget class="QLabel" name="labelLStickLeft"> - <property name="text"> - <string>Left:</string> + <property name="topMargin"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonLStickLeft"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="1" column="2"> - <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonLStickDownHorizontalLayout"> - <item> - <widget class="QLabel" name="labelLStickDown"> - <property name="text"> - <string>Down:</string> + <property name="rightMargin"> + <number>3</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonLStickDown"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="3" column="2"> - <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonLStickModHorizontalLayout"> - <item> - <widget class="QLabel" name="labelLStickMod"> - <property name="text"> - <string>Modifier:</string> + <property name="bottomMargin"> + <number>3</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonLStickMod"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> + <item> + <widget class="QWidget" name="buttonDpadUpWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_23"> + <property name="spacing"> + <number>0</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> + <spacer name="horizontalSpacerDpadUpLeft"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonDpadUpGroup"> + <property name="title"> + <string>Up</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonDpadUp"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Up</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="horizontalSpacerDpadUpRight"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="buttonDpadLeftRightHorizontalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonDpadLeftGroup"> + <property name="title"> + <string>Left</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonDpadLeft"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Left</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonDpadRightGroup"> + <property name="title"> + <string>Right</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonDpadRight"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Right</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QWidget" name="buttonDpadDownWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_24"> + <property name="spacing"> + <number>0</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> + <spacer name="horizontalSpacerDpadDownLeft"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonDpadDownGroup"> + <property name="title"> + <string>Down</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonDpadDown"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Down</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="horizontalSpacerDpadDownRight"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacerBottomLeft_2"> + <property name="orientation"> + <enum>Qt::Vertical</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="1"> - <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0"> - <item> - <layout class="QHBoxLayout" name="buttonLStickPressedHorizontalLayout"> - <item> - <widget class="QLabel" name="labelLStickPressed"> - <property name="text"> - <string>Pressed:</string> + <item> + <widget class="QWidget" name="bottomMiddle" native="true"> + <layout class="QVBoxLayout" stretch="0,0,0"> + <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> + <layout class="QHBoxLayout" name="shoulderButtons"> + <property name="spacing"> + <number>3</number> + </property> + <item> + <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true"> + <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout"> + <property name="spacing"> + <number>0</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="QGroupBox" name="buttonShoulderButtonsButtonLGroup"> + <property name="title"> + <string>L</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonL"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>L</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="buttonShoulderButtonsButtonZLGroup"> + <property name="title"> + <string>ZL</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonZL"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>ZL</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidgetLayout"> + <property name="spacing"> + <number>0</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> + <spacer name="horizontalSpacerShoulderButtons1"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="buttonMiscButtonsMinusScreenshot" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsMinusScreenshotVerticalLayout"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonMiscButtonsMinusGroup"> + <property name="title"> + <string>Minus</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonMinus"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Minus</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonMiscButtonsScreenshotGroup"> + <property name="title"> + <string>Capture</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonScreenshot"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Capture</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="buttonMiscButtonsPlusHome" native="true"> + <layout class="QVBoxLayout" name="buttonMiscButtonsPlusHomeVerticalLayout"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonMiscButtonsPlusGroup"> + <property name="title"> + <string>Plus</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonPlus"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Plus</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonMiscButtonsHomeGroup"> + <property name="title"> + <string>Home</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonHome"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Home</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget3" native="true"> + <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget3Layout"> + <property name="spacing"> + <number>0</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> + <spacer name="horizontalSpacerShoulderButtons2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="buttonShoulderButtonsRight" native="true"> + <layout class="QVBoxLayout" name="buttonShoulderButtonsRightVerticalLayout"> + <property name="spacing"> + <number>0</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="QGroupBox" name="buttonShoulderButtonsRGroup"> + <property name="title"> + <string>R</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonR"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>R</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="buttonShoulderButtonsZRGroup"> + <property name="title"> + <string>ZR</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonZR"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>ZR</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget2" native="true"> + <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget2Layout"> + <property name="spacing"> + <number>0</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> + <spacer name="horizontalSpacerShoulderButtons3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="buttonShoulderButtonsSLSR" native="true"> + <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRVerticalLayout"> + <property name="spacing"> + <number>0</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 alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonShoulderButtonsSLGroup"> + <property name="title"> + <string>SL</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonSL"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>SL</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonShoulderButtonsSRGroup"> + <property name="title"> + <string>SR</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonSR"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>SR</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QFrame" name="controllerFrame"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="styleSheet"> + <string notr="true">image: url(:/controller/pro);</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <property name="leftMargin"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonLStick"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="5" column="1" colspan="2"> - <layout class="QVBoxLayout" name="sliderLStickDeadzoneAndModifierVerticalLayout"> - <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> - </property> - <item> - <layout class="QHBoxLayout" name="sliderLStickDeadzoneAndModifierHorizontalLayout"> - <item> - <widget class="QLabel" name="labelLStickDeadzoneAndModifier"> - <property name="text"> - <string>Deadzone: 0</string> + <property name="topMargin"> + <number>0</number> </property> - <property name="alignment"> - <enum>Qt::AlignHCenter</enum> + <property name="rightMargin"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QSlider" name="sliderLStickDeadzoneAndModifier"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - </layout> - </item> - <item row="6" column="1"> - <spacer name="LStick_verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - </item> - <item row="3" column="0"> - <widget class="QGroupBox" name="shoulderButtons"> - <property name="title"> - <string>Shoulder Buttons</string> - </property> - <property name="flat"> - <bool>false</bool> - </property> - <property name="checkable"> - <bool>false</bool> - </property> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="0" column="0"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout"> - <item> - <widget class="QLabel" name="labelL"> - <property name="text"> - <string>L:</string> + <property name="bottomMargin"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonL"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="miscButtons"> + <property name="spacing"> + <number>3</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <item> + <spacer name="horizontalSpacerMiscButtons1"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QGroupBox" name="buttonMotionLeftGroup"> + <property name="title"> + <string>Motion 1</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout_2"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonMotionLeft"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Left</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="buttonMotionRightGroup"> + <property name="title"> + <string>Motion 2</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout_2"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonMotionRight"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Right</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="horizontalSpacerMiscButtons4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> </item> - <item row="0" column="1"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout"> - <item> - <widget class="QLabel" name="labelR"> - <property name="text"> - <string>R:</string> + <item> + <widget class="QWidget" name="bottomRight" native="true"> + <layout class="QVBoxLayout" name="bottomRightLayout"> + <property name="spacing"> + <number>0</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="QGroupBox" name="faceButtons"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Face Buttons</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonR"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="1" column="0"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout"> - <item> - <widget class="QLabel" name="labelZL"> - <property name="text"> - <string>ZL:</string> + <property name="leftMargin"> + <number>3</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonZL"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="1" column="1"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout"> - <item> - <widget class="QLabel" name="labelZR"> - <property name="text"> - <string>ZR:</string> + <property name="topMargin"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonZR"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="0" column="2"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout"> - <item> - <widget class="QLabel" name="labelSL"> - <property name="text"> - <string>SL:</string> + <property name="rightMargin"> + <number>3</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonSL"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="1" column="2"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout"> - <item> - <widget class="QLabel" name="labelSR"> - <property name="text"> - <string>SR:</string> + <property name="bottomMargin"> + <number>3</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonSR"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item row="3" column="1"> - <widget class="QGroupBox" name="misc"> - <property name="title"> - <string>Misc.</string> - </property> - <property name="flat"> - <bool>false</bool> - </property> - <property name="checkable"> - <bool>false</bool> - </property> - <layout class="QGridLayout" name="gridLayout_6"> - <item row="1" column="0"> - <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonMiscMinusHorizontalLayout"> - <item> - <widget class="QLabel" name="labelMinus"> - <property name="text"> - <string>Minus:</string> + <item> + <widget class="QWidget" name="buttonFaceButtonsBWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_6"> + <property name="spacing"> + <number>0</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> + <spacer name="horizontalSpacerBLeft"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonFaceButtonsXGroup"> + <property name="title"> + <string>X</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonX"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>X</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="horizontalSpacerBRight"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="buttonFaceButtonsYAHorizontalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonFaceButtonsYGroup"> + <property name="title"> + <string>Y</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonY"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Y</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonFaceButtonsAGroup"> + <property name="title"> + <string>A</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonA"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>A</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QWidget" name="buttonFaceButtonsXWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_10"> + <property name="spacing"> + <number>0</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> + <spacer name="horizontalSpacerXLeft"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonFaceButtonsBWidget_2"> + <property name="title"> + <string>B</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonB"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>B</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="horizontalSpacerXRight"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacerBottomRight"> + <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="QGroupBox" name="RStick"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Right Stick</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="spacing"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonMinus"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="3" column="1"> - <spacer name="verticalSpacer_2"> - <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 row="0" column="0"> - <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonMiscPlusHorizontalLayout"> - <item> - <widget class="QLabel" name="labelPlus"> - <property name="text"> - <string>Plus:</string> + <property name="leftMargin"> + <number>3</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonPlus"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="0" column="1"> - <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonMiscHomeHorizontalLayout"> - <item> - <widget class="QLabel" name="labelHome"> - <property name="text"> - <string>Home:</string> + <property name="topMargin"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonHome"> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item row="1" column="1"> - <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout"> - <item> - <layout class="QHBoxLayout" name="buttonMiscScrCapHorizontalLayout"> - <item> - <widget class="QLabel" name="labelScreenshot"> - <property name="text"> - <string>Screen Capture:</string> + <property name="rightMargin"> + <number>3</number> </property> - <property name="wordWrap"> - <bool>false</bool> + <property name="bottomMargin"> + <number>0</number> </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="buttonScreenshot"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> + <item> + <widget class="QWidget" name="buttonRStickUpWidget" native="true"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_9"> + <property name="spacing"> + <number>0</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> + <spacer name="horizontalSpacerRStickUpLeft"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonRStickUpGroup"> + <property name="title"> + <string>Up</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonRStickUp"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Up</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="horizontalSpacerRStickUpRight"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="buttonRStickLeftRightHorizontalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonRStickLeftGroup"> + <property name="title"> + <string>Left</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonRStickLeft"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Left</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonRStickRightGroup"> + <property name="title"> + <string>Right</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonRStickRight"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Right</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QWidget" name="buttonRStickDownWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_11"> + <property name="spacing"> + <number>0</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> + <spacer name="horizontalSpacerRStickDownLeft"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QGroupBox" name="buttonRStickDownGroup"> + <property name="title"> + <string>Down</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonRStickDown"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Down</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="horizontalSpacerRStickDownRight"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="buttonRStickPressedModifierHorizontalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="groupRStickPressed"> + <property name="title"> + <string>Pressed</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonRStick"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Pressed</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignHCenter"> + <widget class="QGroupBox" name="buttonRStickModGroup"> + <property name="title"> + <string>Modifier</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QPushButton" name="buttonRStickMod"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 68px;</string> + </property> + <property name="text"> + <string>Modifier</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="buttonRStickRangeGroup"> + <property name="title"> + <string>Range</string> + </property> + <layout class="QHBoxLayout" name="buttonRStickRangeGroupHorizontalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="spinboxRStickRange"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>50</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="sliderRStickDeadzoneModifierRangeVerticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>2</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout"> + <item> + <widget class="QLabel" name="labelRStickDeadzone"> + <property name="text"> + <string>Deadzone: 0%</string> + </property> + <property name="alignment"> + <set>Qt::AlignHCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QSlider" name="sliderRStickDeadzone"> + <property name="maximum"> + <number>100</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="sliderRStickModifierRangeHorizontalLayout"> + <item> + <widget class="QLabel" name="labelRStickModifierRange"> + <property name="text"> + <string>Modifier Range: 0%</string> + </property> + <property name="alignment"> + <set>Qt::AlignHCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QSlider" name="sliderRStickModifierRange"> + <property name="maximum"> + <number>100</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacerBottomRight_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> </item> </layout> </widget> </item> </layout> </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"/> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QPushButton" name="buttonClearAll"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="sizeIncrement"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <property name="text"> - <string>Clear All</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="buttonRestoreDefaults"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="sizeIncrement"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <property name="text"> - <string>Restore Defaults</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="QDialogButtonBox" name="buttonBox"> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - </item> - </layout> - </item> </layout> </widget> - <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>ConfigureInputPlayer</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>371</x> - <y>730</y> - </hint> - <hint type="destinationlabel"> - <x>229</x> - <y>375</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>ConfigureInputPlayer</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>371</x> - <y>730</y> - </hint> - <hint type="destinationlabel"> - <x>229</x> - <y>375</y> - </hint> - </hints> - </connection> - </connections> + <resources> + <include location="../../../dist/icons/controller/controller.qrc"/> + </resources> + <connections/> </ui> diff --git a/src/yuzu/configuration/configure_input_profile_dialog.cpp b/src/yuzu/configuration/configure_input_profile_dialog.cpp new file mode 100644 index 000000000..1f5cfa75b --- /dev/null +++ b/src/yuzu/configuration/configure_input_profile_dialog.cpp @@ -0,0 +1,37 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "ui_configure_input_profile_dialog.h" +#include "yuzu/configuration/configure_input_player.h" +#include "yuzu/configuration/configure_input_profile_dialog.h" + +ConfigureInputProfileDialog::ConfigureInputProfileDialog( + QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles) + : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()), + profile_widget(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, false)) { + ui->setupUi(this); + + ui->controllerLayout->addWidget(profile_widget); + + connect(ui->clear_all_button, &QPushButton::clicked, this, + [this] { profile_widget->ClearAll(); }); + connect(ui->restore_defaults_button, &QPushButton::clicked, this, + [this] { profile_widget->RestoreDefaults(); }); + + RetranslateUI(); +} + +ConfigureInputProfileDialog::~ConfigureInputProfileDialog() = default; + +void ConfigureInputProfileDialog::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QDialog::changeEvent(event); +} + +void ConfigureInputProfileDialog::RetranslateUI() { + ui->retranslateUi(this); +} diff --git a/src/yuzu/configuration/configure_input_profile_dialog.h b/src/yuzu/configuration/configure_input_profile_dialog.h new file mode 100644 index 000000000..e6386bdbb --- /dev/null +++ b/src/yuzu/configuration/configure_input_profile_dialog.h @@ -0,0 +1,40 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QDialog> + +class QPushButton; + +class ConfigureInputPlayer; + +class InputProfiles; + +namespace InputCommon { +class InputSubsystem; +} + +namespace Ui { +class ConfigureInputProfileDialog; +} + +class ConfigureInputProfileDialog : public QDialog { + Q_OBJECT + +public: + explicit ConfigureInputProfileDialog(QWidget* parent, + InputCommon::InputSubsystem* input_subsystem, + InputProfiles* profiles); + ~ConfigureInputProfileDialog() override; + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + std::unique_ptr<Ui::ConfigureInputProfileDialog> ui; + + ConfigureInputPlayer* profile_widget; +}; diff --git a/src/yuzu/configuration/configure_input_profile_dialog.ui b/src/yuzu/configuration/configure_input_profile_dialog.ui new file mode 100644 index 000000000..726cf6905 --- /dev/null +++ b/src/yuzu/configuration/configure_input_profile_dialog.ui @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureInputProfileDialog</class> + <widget class="QDialog" name="ConfigureInputProfileDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>70</width> + <height>540</height> + </rect> + </property> + <property name="windowTitle"> + <string>Create Input Profile</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>2</number> + </property> + <property name="leftMargin"> + <number>9</number> + </property> + <property name="topMargin"> + <number>9</number> + </property> + <property name="rightMargin"> + <number>9</number> + </property> + <property name="bottomMargin"> + <number>9</number> + </property> + <item> + <layout class="QHBoxLayout" name="controllerLayout"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="clear_all_button"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="restore_defaults_button"> + <property name="text"> + <string>Defaults</string> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ConfigureInputProfileDialog</receiver> + <slot>accept()</slot> + </connection> + </connections> +</ui> diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp deleted file mode 100644 index 0e0e8f113..000000000 --- a/src/yuzu/configuration/configure_input_simple.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <array> -#include <tuple> - -#include "ui_configure_input_simple.h" -#include "yuzu/configuration/configure_input.h" -#include "yuzu/configuration/configure_input_player.h" -#include "yuzu/configuration/configure_input_simple.h" -#include "yuzu/uisettings.h" - -namespace { - -template <typename Dialog, typename... Args> -void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) { - caller->ApplyConfiguration(); - Dialog dialog(caller, std::forward<Args>(args)...); - - const auto res = dialog.exec(); - if (res == QDialog::Accepted) { - dialog.ApplyConfiguration(); - } -} - -// OnProfileSelect functions should (when applicable): -// - Set controller types -// - Set controller enabled -// - Set docked mode -// - Set advanced controller config/enabled (i.e. debug, kbd, mouse, touch) -// -// OnProfileSelect function should NOT however: -// - Reset any button mappings -// - Open any dialogs -// - Block in any way - -constexpr std::size_t PLAYER_0_INDEX = 0; -constexpr std::size_t HANDHELD_INDEX = 8; - -void HandheldOnProfileSelect() { - Settings::values.players[HANDHELD_INDEX].connected = true; - Settings::values.players[HANDHELD_INDEX].type = Settings::ControllerType::DualJoycon; - - for (std::size_t player = 0; player < HANDHELD_INDEX; ++player) { - Settings::values.players[player].connected = false; - } - - Settings::values.use_docked_mode = false; - Settings::values.keyboard_enabled = false; - Settings::values.mouse_enabled = false; - Settings::values.debug_pad_enabled = false; - Settings::values.touchscreen.enabled = true; -} - -void DualJoyconsDockedOnProfileSelect() { - Settings::values.players[PLAYER_0_INDEX].connected = true; - Settings::values.players[PLAYER_0_INDEX].type = Settings::ControllerType::DualJoycon; - - for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) { - Settings::values.players[player].connected = false; - } - - Settings::values.use_docked_mode = true; - Settings::values.keyboard_enabled = false; - Settings::values.mouse_enabled = false; - Settings::values.debug_pad_enabled = false; - Settings::values.touchscreen.enabled = true; -} - -// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure -// is clicked) -using InputProfile = std::tuple<const char*, void (*)(), void (*)(ConfigureInputSimple*)>; - -constexpr std::array<InputProfile, 3> INPUT_PROFILES{{ - {QT_TR_NOOP("Single Player - Handheld - Undocked"), HandheldOnProfileSelect, - [](ConfigureInputSimple* caller) { - CallConfigureDialog<ConfigureInputPlayer>(caller, HANDHELD_INDEX, false); - }}, - {QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect, - [](ConfigureInputSimple* caller) { - CallConfigureDialog<ConfigureInputPlayer>(caller, PLAYER_0_INDEX, false); - }}, - {QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>}, -}}; - -} // namespace - -void ApplyInputProfileConfiguration(int profile_index) { - std::get<1>( - INPUT_PROFILES.at(std::min(profile_index, static_cast<int>(INPUT_PROFILES.size() - 1))))(); -} - -ConfigureInputSimple::ConfigureInputSimple(QWidget* parent) - : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()) { - ui->setupUi(this); - - for (const auto& profile : INPUT_PROFILES) { - const QString label = tr(std::get<0>(profile)); - ui->profile_combobox->addItem(label, label); - } - - connect(ui->profile_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, - &ConfigureInputSimple::OnSelectProfile); - connect(ui->profile_configure, &QPushButton::clicked, this, &ConfigureInputSimple::OnConfigure); - - LoadConfiguration(); -} - -ConfigureInputSimple::~ConfigureInputSimple() = default; - -void ConfigureInputSimple::ApplyConfiguration() { - auto index = ui->profile_combobox->currentIndex(); - // Make the stored index for "Custom" very large so that if new profiles are added it - // doesn't change. - if (index >= static_cast<int>(INPUT_PROFILES.size() - 1)) { - index = std::numeric_limits<int>::max(); - } - - UISettings::values.profile_index = index; -} - -void ConfigureInputSimple::changeEvent(QEvent* event) { - if (event->type() == QEvent::LanguageChange) { - RetranslateUI(); - } - - QWidget::changeEvent(event); -} - -void ConfigureInputSimple::RetranslateUI() { - ui->retranslateUi(this); -} - -void ConfigureInputSimple::LoadConfiguration() { - const auto index = UISettings::values.profile_index; - if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0) { - ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1)); - } else { - ui->profile_combobox->setCurrentIndex(index); - } -} - -void ConfigureInputSimple::OnSelectProfile(int index) { - const auto old_docked = Settings::values.use_docked_mode; - ApplyInputProfileConfiguration(index); - OnDockedModeChanged(old_docked, Settings::values.use_docked_mode); -} - -void ConfigureInputSimple::OnConfigure() { - std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this); -} diff --git a/src/yuzu/configuration/configure_input_simple.h b/src/yuzu/configuration/configure_input_simple.h deleted file mode 100644 index bb5050224..000000000 --- a/src/yuzu/configuration/configure_input_simple.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> - -#include <QWidget> - -class QPushButton; -class QString; -class QTimer; - -namespace Ui { -class ConfigureInputSimple; -} - -// Used by configuration loader to apply a profile if the input is invalid. -void ApplyInputProfileConfiguration(int profile_index); - -class ConfigureInputSimple : public QWidget { - Q_OBJECT - -public: - explicit ConfigureInputSimple(QWidget* parent = nullptr); - ~ConfigureInputSimple() override; - - /// Save all button configurations to settings file - void ApplyConfiguration(); - -private: - void changeEvent(QEvent* event) override; - void RetranslateUI(); - - /// Load configuration settings. - void LoadConfiguration(); - - void OnSelectProfile(int index); - void OnConfigure(); - - std::unique_ptr<Ui::ConfigureInputSimple> ui; -}; diff --git a/src/yuzu/configuration/configure_input_simple.ui b/src/yuzu/configuration/configure_input_simple.ui deleted file mode 100644 index c4889caa9..000000000 --- a/src/yuzu/configuration/configure_input_simple.ui +++ /dev/null @@ -1,97 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>ConfigureInputSimple</class> - <widget class="QWidget" name="ConfigureInputSimple"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>473</width> - <height>685</height> - </rect> - </property> - <property name="windowTitle"> - <string>ConfigureInputSimple</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_5"> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QGroupBox" name="gridGroupBox"> - <property name="title"> - <string>Profile</string> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="1" column="2"> - <widget class="QPushButton" name="profile_configure"> - <property name="text"> - <string>Configure</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <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 row="1" column="3"> - <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> - <item row="1" column="1"> - <widget class="QComboBox" name="profile_combobox"> - <property name="minimumSize"> - <size> - <width>250</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="0" column="1" colspan="2"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Choose a controller configuration:</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp new file mode 100644 index 000000000..170574d9b --- /dev/null +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -0,0 +1,314 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <array> +#include <QCloseEvent> +#include <QLabel> +#include <QMessageBox> +#include <QPushButton> +#include <QVBoxLayout> +#include "common/logging/log.h" +#include "core/settings.h" +#include "input_common/main.h" +#include "input_common/udp/client.h" +#include "input_common/udp/udp.h" +#include "ui_configure_motion_touch.h" +#include "yuzu/configuration/configure_motion_touch.h" +#include "yuzu/configuration/configure_touch_from_button.h" + +CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent, + const std::string& host, u16 port, + u8 pad_index, u16 client_id) + : QDialog(parent) { + layout = new QVBoxLayout; + status_label = new QLabel(tr("Communicating with the server...")); + cancel_button = new QPushButton(tr("Cancel")); + connect(cancel_button, &QPushButton::clicked, this, [this] { + if (!completed) { + job->Stop(); + } + accept(); + }); + layout->addWidget(status_label); + layout->addWidget(cancel_button); + setLayout(layout); + + using namespace InputCommon::CemuhookUDP; + job = std::make_unique<CalibrationConfigurationJob>( + host, port, pad_index, client_id, + [this](CalibrationConfigurationJob::Status status) { + QString text; + switch (status) { + case CalibrationConfigurationJob::Status::Ready: + text = tr("Touch the top left corner <br>of your touchpad."); + break; + case CalibrationConfigurationJob::Status::Stage1Completed: + text = tr("Now touch the bottom right corner <br>of your touchpad."); + break; + case CalibrationConfigurationJob::Status::Completed: + text = tr("Configuration completed!"); + break; + } + QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text)); + if (status == CalibrationConfigurationJob::Status::Completed) { + QMetaObject::invokeMethod(this, "UpdateButtonText", Q_ARG(QString, tr("OK"))); + } + }, + [this](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) { + completed = true; + min_x = min_x_; + min_y = min_y_; + max_x = max_x_; + max_y = max_y_; + }); +} + +CalibrationConfigurationDialog::~CalibrationConfigurationDialog() = default; + +void CalibrationConfigurationDialog::UpdateLabelText(const QString& text) { + status_label->setText(text); +} + +void CalibrationConfigurationDialog::UpdateButtonText(const QString& text) { + cancel_button->setText(text); +} + +constexpr std::array<std::pair<const char*, const char*>, 2> MotionProviders = {{ + {"motion_emu", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Mouse (Right Click)")}, + {"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")}, +}}; + +constexpr std::array<std::pair<const char*, const char*>, 2> TouchProviders = {{ + {"emu_window", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Emulator Window")}, + {"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")}, +}}; + +ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent, + InputCommon::InputSubsystem* input_subsystem_) + : QDialog(parent), input_subsystem{input_subsystem_}, + ui(std::make_unique<Ui::ConfigureMotionTouch>()) { + ui->setupUi(this); + for (const auto& [provider, name] : MotionProviders) { + ui->motion_provider->addItem(tr(name), QString::fromUtf8(provider)); + } + for (const auto& [provider, name] : TouchProviders) { + ui->touch_provider->addItem(tr(name), QString::fromUtf8(provider)); + } + + ui->udp_learn_more->setOpenExternalLinks(true); + ui->udp_learn_more->setText( + tr("<a " + "href='https://yuzu-emu.org/wiki/" + "using-a-controller-or-android-phone-for-motion-or-touch-input'><span " + "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); + + SetConfiguration(); + UpdateUiDisplay(); + ConnectEvents(); +} + +ConfigureMotionTouch::~ConfigureMotionTouch() = default; + +void ConfigureMotionTouch::SetConfiguration() { + const Common::ParamPackage motion_param(Settings::values.motion_device); + const Common::ParamPackage touch_param(Settings::values.touch_device); + const std::string motion_engine = motion_param.Get("engine", "motion_emu"); + const std::string touch_engine = touch_param.Get("engine", "emu_window"); + + ui->motion_provider->setCurrentIndex( + ui->motion_provider->findData(QString::fromStdString(motion_engine))); + ui->touch_provider->setCurrentIndex( + ui->touch_provider->findData(QString::fromStdString(touch_engine))); + ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button); + touch_from_button_maps = Settings::values.touch_from_button_maps; + for (const auto& touch_map : touch_from_button_maps) { + ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); + } + ui->touch_from_button_map->setCurrentIndex(Settings::values.touch_from_button_map_index); + ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f)); + + min_x = touch_param.Get("min_x", 100); + min_y = touch_param.Get("min_y", 50); + max_x = touch_param.Get("max_x", 1800); + max_y = touch_param.Get("max_y", 850); + + ui->udp_server->setText(QString::fromStdString(Settings::values.udp_input_address)); + ui->udp_port->setText(QString::number(Settings::values.udp_input_port)); + ui->udp_pad_index->setCurrentIndex(Settings::values.udp_pad_index); +} + +void ConfigureMotionTouch::UpdateUiDisplay() { + const QString motion_engine = ui->motion_provider->currentData().toString(); + const QString touch_engine = ui->touch_provider->currentData().toString(); + const QString cemuhook_udp = QStringLiteral("cemuhookudp"); + + if (motion_engine == QStringLiteral("motion_emu")) { + ui->motion_sensitivity_label->setVisible(true); + ui->motion_sensitivity->setVisible(true); + } else { + ui->motion_sensitivity_label->setVisible(false); + ui->motion_sensitivity->setVisible(false); + } + + if (touch_engine == cemuhook_udp) { + ui->touch_calibration->setVisible(true); + ui->touch_calibration_config->setVisible(true); + ui->touch_calibration_label->setVisible(true); + ui->touch_calibration->setText( + QStringLiteral("(%1, %2) - (%3, %4)").arg(min_x).arg(min_y).arg(max_x).arg(max_y)); + } else { + ui->touch_calibration->setVisible(false); + ui->touch_calibration_config->setVisible(false); + ui->touch_calibration_label->setVisible(false); + } + + if (motion_engine == cemuhook_udp || touch_engine == cemuhook_udp) { + ui->udp_config_group_box->setVisible(true); + } else { + ui->udp_config_group_box->setVisible(false); + } +} + +void ConfigureMotionTouch::ConnectEvents() { + connect(ui->motion_provider, qOverload<int>(&QComboBox::currentIndexChanged), this, + [this](int index) { UpdateUiDisplay(); }); + connect(ui->touch_provider, qOverload<int>(&QComboBox::currentIndexChanged), this, + [this](int index) { UpdateUiDisplay(); }); + connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest); + connect(ui->touch_calibration_config, &QPushButton::clicked, this, + &ConfigureMotionTouch::OnConfigureTouchCalibration); + connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this, + &ConfigureMotionTouch::OnConfigureTouchFromButton); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { + if (CanCloseDialog()) { + reject(); + } + }); +} + +void ConfigureMotionTouch::OnCemuhookUDPTest() { + ui->udp_test->setEnabled(false); + ui->udp_test->setText(tr("Testing")); + udp_test_in_progress = true; + InputCommon::CemuhookUDP::TestCommunication( + ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toInt()), + static_cast<u32>(ui->udp_pad_index->currentIndex()), 24872, + [this] { + LOG_INFO(Frontend, "UDP input test success"); + QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true)); + }, + [this] { + LOG_ERROR(Frontend, "UDP input test failed"); + QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, false)); + }); +} + +void ConfigureMotionTouch::OnConfigureTouchCalibration() { + ui->touch_calibration_config->setEnabled(false); + ui->touch_calibration_config->setText(tr("Configuring")); + CalibrationConfigurationDialog dialog( + this, ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toUInt()), + static_cast<u8>(ui->udp_pad_index->currentIndex()), 24872); + dialog.exec(); + if (dialog.completed) { + min_x = dialog.min_x; + min_y = dialog.min_y; + max_x = dialog.max_x; + max_y = dialog.max_y; + LOG_INFO(Frontend, + "UDP touchpad calibration config success: min_x={}, min_y={}, max_x={}, max_y={}", + min_x, min_y, max_x, max_y); + UpdateUiDisplay(); + } else { + LOG_ERROR(Frontend, "UDP touchpad calibration config failed"); + } + ui->touch_calibration_config->setEnabled(true); + ui->touch_calibration_config->setText(tr("Configure")); +} + +void ConfigureMotionTouch::closeEvent(QCloseEvent* event) { + if (CanCloseDialog()) { + event->accept(); + } else { + event->ignore(); + } +} + +void ConfigureMotionTouch::ShowUDPTestResult(bool result) { + udp_test_in_progress = false; + if (result) { + QMessageBox::information(this, tr("Test Successful"), + tr("Successfully received data from the server.")); + } else { + QMessageBox::warning(this, tr("Test Failed"), + tr("Could not receive valid data from the server.<br>Please verify " + "that the server is set up correctly and " + "the address and port are correct.")); + } + ui->udp_test->setEnabled(true); + ui->udp_test->setText(tr("Test")); +} + +void ConfigureMotionTouch::OnConfigureTouchFromButton() { + ConfigureTouchFromButton dialog{this, touch_from_button_maps, input_subsystem, + ui->touch_from_button_map->currentIndex()}; + if (dialog.exec() != QDialog::Accepted) { + return; + } + touch_from_button_maps = dialog.GetMaps(); + + while (ui->touch_from_button_map->count() > 0) { + ui->touch_from_button_map->removeItem(0); + } + for (const auto& touch_map : touch_from_button_maps) { + ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); + } + ui->touch_from_button_map->setCurrentIndex(dialog.GetSelectedIndex()); +} + +bool ConfigureMotionTouch::CanCloseDialog() { + if (udp_test_in_progress) { + QMessageBox::warning(this, tr("Citra"), + tr("UDP Test or calibration configuration is in progress.<br>Please " + "wait for them to finish.")); + return false; + } + return true; +} + +void ConfigureMotionTouch::ApplyConfiguration() { + if (!CanCloseDialog()) { + return; + } + + std::string motion_engine = ui->motion_provider->currentData().toString().toStdString(); + std::string touch_engine = ui->touch_provider->currentData().toString().toStdString(); + + Common::ParamPackage motion_param{}, touch_param{}; + motion_param.Set("engine", std::move(motion_engine)); + touch_param.Set("engine", std::move(touch_engine)); + + if (motion_engine == "motion_emu") { + motion_param.Set("sensitivity", static_cast<float>(ui->motion_sensitivity->value())); + } + + if (touch_engine == "cemuhookudp") { + touch_param.Set("min_x", min_x); + touch_param.Set("min_y", min_y); + touch_param.Set("max_x", max_x); + touch_param.Set("max_y", max_y); + } + + Settings::values.motion_device = motion_param.Serialize(); + Settings::values.touch_device = touch_param.Serialize(); + Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked(); + Settings::values.touch_from_button_map_index = ui->touch_from_button_map->currentIndex(); + Settings::values.touch_from_button_maps = touch_from_button_maps; + Settings::values.udp_input_address = ui->udp_server->text().toStdString(); + Settings::values.udp_input_port = static_cast<u16>(ui->udp_port->text().toInt()); + Settings::values.udp_pad_index = static_cast<u8>(ui->udp_pad_index->currentIndex()); + input_subsystem->ReloadInputDevices(); + + accept(); +} diff --git a/src/yuzu/configuration/configure_motion_touch.h b/src/yuzu/configuration/configure_motion_touch.h new file mode 100644 index 000000000..3d4b5d659 --- /dev/null +++ b/src/yuzu/configuration/configure_motion_touch.h @@ -0,0 +1,90 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QDialog> +#include "common/param_package.h" + +class QLabel; +class QPushButton; +class QVBoxLayout; + +namespace InputCommon { +class InputSubsystem; +} + +namespace InputCommon::CemuhookUDP { +class CalibrationConfigurationJob; +} + +namespace Ui { +class ConfigureMotionTouch; +} + +/// A dialog for touchpad calibration configuration. +class CalibrationConfigurationDialog : public QDialog { + Q_OBJECT +public: + explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port, + u8 pad_index, u16 client_id); + ~CalibrationConfigurationDialog() override; + +private: + Q_INVOKABLE void UpdateLabelText(const QString& text); + Q_INVOKABLE void UpdateButtonText(const QString& text); + + QVBoxLayout* layout; + QLabel* status_label; + QPushButton* cancel_button; + std::unique_ptr<InputCommon::CemuhookUDP::CalibrationConfigurationJob> job; + + // Configuration results + bool completed{}; + u16 min_x{}; + u16 min_y{}; + u16 max_x{}; + u16 max_y{}; + + friend class ConfigureMotionTouch; +}; + +class ConfigureMotionTouch : public QDialog { + Q_OBJECT + +public: + explicit ConfigureMotionTouch(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_); + ~ConfigureMotionTouch() override; + +public slots: + void ApplyConfiguration(); + +private slots: + void OnCemuhookUDPTest(); + void OnConfigureTouchCalibration(); + void OnConfigureTouchFromButton(); + +private: + void closeEvent(QCloseEvent* event) override; + Q_INVOKABLE void ShowUDPTestResult(bool result); + void SetConfiguration(); + void UpdateUiDisplay(); + void ConnectEvents(); + bool CanCloseDialog(); + + InputCommon::InputSubsystem* input_subsystem; + + std::unique_ptr<Ui::ConfigureMotionTouch> ui; + + // Coordinate system of the CemuhookUDP touch provider + int min_x{}; + int min_y{}; + int max_x{}; + int max_y{}; + + bool udp_test_in_progress{}; + + std::vector<Settings::TouchFromButtonMap> touch_from_button_maps; +}; diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui new file mode 100644 index 000000000..5b78c5a4b --- /dev/null +++ b/src/yuzu/configuration/configure_motion_touch.ui @@ -0,0 +1,317 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureMotionTouch</class> + <widget class="QDialog" name="ConfigureMotionTouch"> + <property name="windowTitle"> + <string>Configure Motion / Touch</string> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>500</width> + <height>450</height> + </rect> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="QGroupBox" name="motion_group_box"> + <property name="title"> + <string>Motion</string> + </property> + <layout class="QVBoxLayout"> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="motion_provider_label"> + <property name="text"> + <string>Motion Provider:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="motion_provider"/> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="motion_sensitivity_label"> + <property name="text"> + <string>Sensitivity:</string> + </property> + </widget> + </item> + <item> + <widget class="QDoubleSpinBox" name="motion_sensitivity"> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="decimals"> + <number>4</number> + </property> + <property name="minimum"> + <double>0.010000000000000</double> + </property> + <property name="maximum"> + <double>10.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.001000000000000</double> + </property> + <property name="value"> + <double>0.010000000000000</double> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="touch_group_box"> + <property name="title"> + <string>Touch</string> + </property> + <layout class="QVBoxLayout"> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="touch_provider_label"> + <property name="text"> + <string>Touch Provider:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="touch_provider"/> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="touch_calibration_label"> + <property name="text"> + <string>Calibration:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="touch_calibration"> + <property name="text"> + <string>(100, 50) - (1800, 850)</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="touch_calibration_config"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Configure</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QCheckBox" name="touch_from_button_checkbox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Use button mapping:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="touch_from_button_map"/> + </item> + <item> + <widget class="QPushButton" name="touch_from_button_config_btn"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Configure</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="udp_config_group_box"> + <property name="title"> + <string>CemuhookUDP Config</string> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="QLabel" name="udp_help"> + <property name="text"> + <string>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="udp_server_label"> + <property name="text"> + <string>Server:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="udp_server"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="udp_port_label"> + <property name="text"> + <string>Port:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="udp_port"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="udp_pad_index_label"> + <property name="text"> + <string>Pad:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="udp_pad_index"> + <item> + <property name="text"> + <string>Pad 1</string> + </property> + </item> + <item> + <property name="text"> + <string>Pad 2</string> + </property> + </item> + <item> + <property name="text"> + <string>Pad 3</string> + </property> + </item> + <item> + <property name="text"> + <string>Pad 4</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="udp_learn_more"> + <property name="text"> + <string>Learn More</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="udp_test"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Test</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>167</width> + <height>55</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ConfigureMotionTouch</receiver> + <slot>ApplyConfiguration()</slot> + </connection> + </connections> +</ui> diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp index 5bcf5ffa8..2af3afda8 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.cpp +++ b/src/yuzu/configuration/configure_mouse_advanced.cpp @@ -18,6 +18,16 @@ static QString GetKeyName(int key_code) { switch (key_code) { + case Qt::LeftButton: + return QObject::tr("Click 0"); + case Qt::RightButton: + return QObject::tr("Click 1"); + case Qt::MiddleButton: + return QObject::tr("Click 2"); + case Qt::BackButton: + return QObject::tr("Click 3"); + case Qt::ForwardButton: + return QObject::tr("Click 4"); case Qt::Key_Shift: return QObject::tr("Shift"); case Qt::Key_Control: @@ -66,8 +76,10 @@ static QString ButtonToText(const Common::ParamPackage& param) { return QObject::tr("[unknown]"); } -ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) - : QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), +ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent, + InputCommon::InputSubsystem* input_subsystem_) + : QDialog(parent), + ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { ui->setupUi(this); setFocusPolicy(Qt::ClickFocus); @@ -188,9 +200,9 @@ void ConfigureMouseAdvanced::HandleClick( button->setText(tr("[press key]")); button->setFocus(); - // Keyboard keys can only be used as button devices - want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; - if (want_keyboard_keys) { + // Keyboard keys or mouse buttons can only be used as button devices + want_keyboard_mouse = type == InputCommon::Polling::DeviceType::Button; + if (want_keyboard_mouse) { const auto iter = std::find(button_map.begin(), button_map.end(), button); ASSERT(iter != button_map.end()); const auto index = std::distance(button_map.begin(), iter); @@ -199,27 +211,29 @@ void ConfigureMouseAdvanced::HandleClick( input_setter = new_input_setter; - device_pollers = InputCommon::Polling::GetPollers(type); + device_pollers = input_subsystem->GetPollers(type); for (auto& poller : device_pollers) { poller->Start(); } - grabKeyboard(); - grabMouse(); - timeout_timer->start(5000); // Cancel after 5 seconds - poll_timer->start(200); // Check for new inputs every 200ms + QWidget::grabMouse(); + QWidget::grabKeyboard(); + + timeout_timer->start(2500); // Cancel after 2.5 seconds + poll_timer->start(50); // Check for new inputs every 50ms } void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) { - releaseKeyboard(); - releaseMouse(); timeout_timer->stop(); poll_timer->stop(); for (auto& poller : device_pollers) { poller->Stop(); } + QWidget::releaseMouse(); + QWidget::releaseKeyboard(); + if (!abort) { (*input_setter)(params); } @@ -228,13 +242,29 @@ void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params input_setter = std::nullopt; } +void ConfigureMouseAdvanced::mousePressEvent(QMouseEvent* event) { + if (!input_setter || !event) { + return; + } + + if (want_keyboard_mouse) { + SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())}, + false); + } else { + // We don't want any mouse buttons, so don't stop polling + return; + } + + SetPollingResult({}, true); +} + void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) { if (!input_setter || !event) { return; } if (event->key() != Qt::Key_Escape) { - if (want_keyboard_keys) { + if (want_keyboard_mouse) { SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, false); } else { diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h index 342b82412..65b6fca9a 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.h +++ b/src/yuzu/configuration/configure_mouse_advanced.h @@ -8,12 +8,14 @@ #include <optional> #include <QDialog> -#include "core/settings.h" - class QCheckBox; class QPushButton; class QTimer; +namespace InputCommon { +class InputSubsystem; +} + namespace Ui { class ConfigureMouseAdvanced; } @@ -22,7 +24,7 @@ class ConfigureMouseAdvanced : public QDialog { Q_OBJECT public: - explicit ConfigureMouseAdvanced(QWidget* parent); + explicit ConfigureMouseAdvanced(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_); ~ConfigureMouseAdvanced() override; void ApplyConfiguration(); @@ -49,11 +51,16 @@ private: /// Finish polling and configure input using the input_setter void SetPollingResult(const Common::ParamPackage& params, bool abort); + /// Handle mouse button press events. + void mousePressEvent(QMouseEvent* event) override; + /// Handle key press events. void keyPressEvent(QKeyEvent* event) override; std::unique_ptr<Ui::ConfigureMouseAdvanced> ui; + InputCommon::InputSubsystem* input_subsystem; + /// This will be the the setting function when an input is awaiting configuration. std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; @@ -67,5 +74,5 @@ private: /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, /// keyboard events are ignored. - bool want_keyboard_keys = false; + bool want_keyboard_mouse = false; }; diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui index 08245ecf0..5b99e1c37 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.ui +++ b/src/yuzu/configuration/configure_mouse_advanced.ui @@ -6,13 +6,18 @@ <rect> <x>0</x> <y>0</y> - <width>250</width> - <height>261</height> + <width>310</width> + <height>193</height> </rect> </property> <property name="windowTitle"> <string>Configure Mouse</string> </property> + <property name="styleSheet"> + <string notr="true">QPushButton { + min-width: 60px; +}</string> + </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QGroupBox" name="gridGroupBox"> @@ -20,81 +25,33 @@ <string>Mouse Buttons</string> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="4"> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="0" column="3"> - <layout class="QVBoxLayout" name="verticalLayout_4"> + <item row="3" column="5"> + <layout class="QVBoxLayout" name="verticalLayout_6"> <item> - <layout class="QHBoxLayout" name="horizontalLayout_3"> + <layout class="QHBoxLayout" name="horizontalLayout_5"> <item> - <widget class="QLabel" name="label_3"> + <widget class="QLabel" name="label_5"> <property name="text"> - <string>Right:</string> + <string>Forward:</string> </property> </widget> </item> </layout> </item> <item> - <widget class="QPushButton" name="right_button"> + <widget class="QPushButton" name="forward_button"> <property name="minimumSize"> <size> - <width>75</width> + <width>68</width> <height>0</height> </size> </property> - <property name="text"> - <string/> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> </property> - </widget> - </item> - </layout> - </item> - <item row="0" column="0"> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="2" column="1"> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Middle:</string> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="middle_button"> <property name="text"> <string/> </property> @@ -123,6 +80,12 @@ </item> <item> <widget class="QPushButton" name="back_button"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> <property name="text"> <string/> </property> @@ -147,7 +110,7 @@ <widget class="QPushButton" name="left_button"> <property name="minimumSize"> <size> - <width>75</width> + <width>68</width> <height>0</height> </size> </property> @@ -158,21 +121,99 @@ </item> </layout> </item> - <item row="3" column="3"> - <layout class="QVBoxLayout" name="verticalLayout_6"> + <item row="0" column="3"> + <layout class="QVBoxLayout" name="verticalLayout_3"> <item> - <layout class="QHBoxLayout" name="horizontalLayout_5"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> - <widget class="QLabel" name="label_5"> + <widget class="QLabel" name="label_2"> <property name="text"> - <string>Forward:</string> + <string>Middle:</string> </property> </widget> </item> </layout> </item> <item> - <widget class="QPushButton" name="forward_button"> + <widget class="QPushButton" name="middle_button"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="6"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="5"> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Right:</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QPushButton" name="right_button"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> <property name="text"> <string/> </property> @@ -180,6 +221,32 @@ </item> </layout> </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="4"> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> </layout> </widget> </item> @@ -187,15 +254,39 @@ <layout class="QHBoxLayout" name="horizontalLayout_6"> <item> <widget class="QPushButton" name="buttonClearAll"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> <property name="text"> - <string>Clear All</string> + <string>Clear</string> </property> </widget> </item> <item> <widget class="QPushButton" name="buttonRestoreDefaults"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> <property name="text"> - <string>Restore Defaults</string> + <string>Defaults</string> </property> </widget> </item> @@ -206,21 +297,24 @@ </property> <property name="sizeHint" stdset="0"> <size> - <width>40</width> + <width>0</width> <height>20</height> </size> </property> </spacer> </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="styleSheet"> + <string notr="true"/> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> </layout> </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - </item> </layout> </widget> <resources/> @@ -230,32 +324,12 @@ <signal>accepted()</signal> <receiver>ConfigureMouseAdvanced</receiver> <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>124</x> - <y>266</y> - </hint> - <hint type="destinationlabel"> - <x>124</x> - <y>143</y> - </hint> - </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>ConfigureMouseAdvanced</receiver> <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>124</x> - <y>266</y> - </hint> - <hint type="destinationlabel"> - <x>124</x> - <y>143</y> - </hint> - </hints> </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 1e49f0787..8eac3bd9d 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -16,6 +16,7 @@ #include "common/common_paths.h" #include "common/file_util.h" +#include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/xts_archive.h" @@ -29,9 +30,10 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id) : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) { - game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false); + game_config = std::make_unique<Config>(fmt::format("{:016X}", title_id), + Config::ConfigType::PerGameConfig); - Settings::configuring_global = false; + Settings::SetConfiguringGlobal(false); ui->setupUi(this); setFocusPolicy(Qt::ClickFocus); @@ -88,9 +90,11 @@ void ConfigurePerGame::LoadConfiguration() { ui->display_title_id->setText( QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper()); - FileSys::PatchManager pm{title_id}; + auto& system = Core::System::GetInstance(); + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; const auto control = pm.GetControlMetadata(); - const auto loader = Loader::GetLoader(file); + const auto loader = Loader::GetLoader(system, file); if (control.first != nullptr) { ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui index d2057c4ab..25975b3b9 100644 --- a/src/yuzu/configuration/configure_per_game.ui +++ b/src/yuzu/configuration/configure_per_game.ui @@ -319,32 +319,12 @@ <signal>accepted()</signal> <receiver>ConfigurePerGame</receiver> <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>248</x> - <y>254</y> - </hint> - <hint type="destinationlabel"> - <x>157</x> - <y>274</y> - </hint> - </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>ConfigurePerGame</receiver> <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>316</x> - <y>260</y> - </hint> - <hint type="destinationlabel"> - <x>286</x> - <y>274</y> - </hint> - </hints> </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp index 793fd8975..cdeeec01c 100644 --- a/src/yuzu/configuration/configure_per_game_addons.cpp +++ b/src/yuzu/configuration/configure_per_game_addons.cpp @@ -112,8 +112,10 @@ void ConfigurePerGameAddons::LoadConfiguration() { return; } - FileSys::PatchManager pm{title_id}; - const auto loader = Loader::GetLoader(file); + auto& system = Core::System::GetInstance(); + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; + const auto loader = Loader::GetLoader(system, file); FileSys::VirtualFile update_raw; loader->ReadUpdateRaw(update_raw); diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 9ad43ed8f..59a58d92c 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/file_util.h" #include "core/core.h" +#include "core/hle/service/time/time.h" #include "core/settings.h" #include "ui_configure_system.h" #include "yuzu/configuration/configuration_shared.h" @@ -36,8 +37,8 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui:: } }); - ui->label_console_id->setVisible(Settings::configuring_global); - ui->button_regenerate_console_id->setVisible(Settings::configuring_global); + ui->label_console_id->setVisible(Settings::IsConfiguringGlobal()); + ui->button_regenerate_console_id->setVisible(Settings::IsConfiguringGlobal()); SetupPerGameUI(); @@ -77,7 +78,7 @@ void ConfigureSystem::SetConfiguration() { Settings::values.rng_seed.UsingGlobal()); ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); - if (Settings::configuring_global) { + 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()); @@ -104,11 +105,27 @@ void ConfigureSystem::SetConfiguration() { void ConfigureSystem::ReadSystemSettings() {} void ConfigureSystem::ApplyConfiguration() { + // Allow setting custom RTC even if system is powered on, to allow in-game time to be fast + // forwared + if (Settings::values.custom_rtc.UsingGlobal()) { + if (ui->custom_rtc_checkbox->isChecked()) { + Settings::values.custom_rtc.SetValue( + std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch())); + if (Core::System::GetInstance().IsPoweredOn()) { + const s64 posix_time{Settings::values.custom_rtc.GetValue()->count() + + Service::Time::TimeManager::GetExternalTimeZoneOffset()}; + Core::System::GetInstance().GetTimeManager().UpdateLocalSystemClockTime(posix_time); + } + } else { + Settings::values.custom_rtc.SetValue(std::nullopt); + } + } + if (!enabled) { return; } - if (Settings::configuring_global) { + if (Settings::IsConfiguringGlobal()) { // Guard if during game and set to game-specific value if (Settings::values.language_index.UsingGlobal()) { Settings::values.language_index.SetValue(ui->combo_language->currentIndex()); @@ -131,15 +148,6 @@ void ConfigureSystem::ApplyConfiguration() { Settings::values.rng_seed.SetValue(std::nullopt); } } - - if (Settings::values.custom_rtc.UsingGlobal()) { - if (ui->custom_rtc_checkbox->isChecked()) { - Settings::values.custom_rtc.SetValue( - std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch())); - } else { - Settings::values.custom_rtc.SetValue(std::nullopt); - } - } } else { ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index, ui->combo_language); @@ -210,7 +218,7 @@ void ConfigureSystem::RefreshConsoleID() { } void ConfigureSystem::SetupPerGameUI() { - if (Settings::configuring_global) { + 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()); diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp new file mode 100644 index 000000000..15557e4b8 --- /dev/null +++ b/src/yuzu/configuration/configure_touch_from_button.cpp @@ -0,0 +1,623 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QInputDialog> +#include <QKeyEvent> +#include <QMessageBox> +#include <QMouseEvent> +#include <QResizeEvent> +#include <QStandardItemModel> +#include <QTimer> +#include "common/param_package.h" +#include "core/frontend/framebuffer_layout.h" +#include "core/settings.h" +#include "input_common/main.h" +#include "ui_configure_touch_from_button.h" +#include "yuzu/configuration/configure_touch_from_button.h" +#include "yuzu/configuration/configure_touch_widget.h" + +static QString GetKeyName(int key_code) { + switch (key_code) { + case Qt::Key_Shift: + return QObject::tr("Shift"); + case Qt::Key_Control: + return QObject::tr("Ctrl"); + case Qt::Key_Alt: + return QObject::tr("Alt"); + case Qt::Key_Meta: + return QString{}; + default: + return QKeySequence(key_code).toString(); + } +} + +static QString ButtonToText(const Common::ParamPackage& param) { + if (!param.Has("engine")) { + return QObject::tr("[not set]"); + } + + if (param.Get("engine", "") == "keyboard") { + return GetKeyName(param.Get("code", 0)); + } + + if (param.Get("engine", "") == "sdl") { + if (param.Has("hat")) { + const QString hat_str = QString::fromStdString(param.Get("hat", "")); + const QString direction_str = QString::fromStdString(param.Get("direction", "")); + + return QObject::tr("Hat %1 %2").arg(hat_str, direction_str); + } + + if (param.Has("axis")) { + const QString axis_str = QString::fromStdString(param.Get("axis", "")); + const QString direction_str = QString::fromStdString(param.Get("direction", "")); + + return QObject::tr("Axis %1%2").arg(axis_str, direction_str); + } + + if (param.Has("button")) { + const QString button_str = QString::fromStdString(param.Get("button", "")); + + return QObject::tr("Button %1").arg(button_str); + } + + return {}; + } + + return QObject::tr("[unknown]"); +} + +ConfigureTouchFromButton::ConfigureTouchFromButton( + QWidget* parent, const std::vector<Settings::TouchFromButtonMap>& touch_maps, + InputCommon::InputSubsystem* input_subsystem_, const int default_index) + : QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchFromButton>()), + touch_maps(touch_maps), input_subsystem{input_subsystem_}, selected_index(default_index), + timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { + ui->setupUi(this); + binding_list_model = new QStandardItemModel(0, 3, this); + binding_list_model->setHorizontalHeaderLabels( + {tr("Button"), tr("X", "X axis"), tr("Y", "Y axis")}); + ui->binding_list->setModel(binding_list_model); + ui->bottom_screen->SetCoordLabel(ui->coord_label); + + SetConfiguration(); + UpdateUiDisplay(); + ConnectEvents(); +} + +ConfigureTouchFromButton::~ConfigureTouchFromButton() = default; + +void ConfigureTouchFromButton::showEvent(QShowEvent* ev) { + QWidget::showEvent(ev); + + // width values are not valid in the constructor + const int w = + ui->binding_list->viewport()->contentsRect().width() / binding_list_model->columnCount(); + if (w <= 0) { + return; + } + ui->binding_list->setColumnWidth(0, w); + ui->binding_list->setColumnWidth(1, w); + ui->binding_list->setColumnWidth(2, w); +} + +void ConfigureTouchFromButton::SetConfiguration() { + for (const auto& touch_map : touch_maps) { + ui->mapping->addItem(QString::fromStdString(touch_map.name)); + } + + ui->mapping->setCurrentIndex(selected_index); +} + +void ConfigureTouchFromButton::UpdateUiDisplay() { + ui->button_delete->setEnabled(touch_maps.size() > 1); + ui->button_delete_bind->setEnabled(false); + + binding_list_model->removeRows(0, binding_list_model->rowCount()); + + for (const auto& button_str : touch_maps[selected_index].buttons) { + Common::ParamPackage package{button_str}; + QStandardItem* button = new QStandardItem(ButtonToText(package)); + button->setData(QString::fromStdString(button_str)); + button->setEditable(false); + QStandardItem* xcoord = new QStandardItem(QString::number(package.Get("x", 0))); + QStandardItem* ycoord = new QStandardItem(QString::number(package.Get("y", 0))); + binding_list_model->appendRow({button, xcoord, ycoord}); + + const int dot = ui->bottom_screen->AddDot(package.Get("x", 0), package.Get("y", 0)); + button->setData(dot, DataRoleDot); + } +} + +void ConfigureTouchFromButton::ConnectEvents() { + connect(ui->mapping, qOverload<int>(&QComboBox::currentIndexChanged), this, [this](int index) { + SaveCurrentMapping(); + selected_index = index; + UpdateUiDisplay(); + }); + connect(ui->button_new, &QPushButton::clicked, this, &ConfigureTouchFromButton::NewMapping); + connect(ui->button_delete, &QPushButton::clicked, this, + &ConfigureTouchFromButton::DeleteMapping); + connect(ui->button_rename, &QPushButton::clicked, this, + &ConfigureTouchFromButton::RenameMapping); + connect(ui->button_delete_bind, &QPushButton::clicked, this, + &ConfigureTouchFromButton::DeleteBinding); + connect(ui->binding_list, &QTreeView::doubleClicked, this, + &ConfigureTouchFromButton::EditBinding); + connect(ui->binding_list->selectionModel(), &QItemSelectionModel::selectionChanged, this, + &ConfigureTouchFromButton::OnBindingSelection); + connect(binding_list_model, &QStandardItemModel::itemChanged, this, + &ConfigureTouchFromButton::OnBindingChanged); + connect(ui->binding_list->model(), &QStandardItemModel::rowsAboutToBeRemoved, this, + &ConfigureTouchFromButton::OnBindingDeleted); + connect(ui->bottom_screen, &TouchScreenPreview::DotAdded, this, + &ConfigureTouchFromButton::NewBinding); + connect(ui->bottom_screen, &TouchScreenPreview::DotSelected, this, + &ConfigureTouchFromButton::SetActiveBinding); + connect(ui->bottom_screen, &TouchScreenPreview::DotMoved, this, + &ConfigureTouchFromButton::SetCoordinates); + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, + &ConfigureTouchFromButton::ApplyConfiguration); + + connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); }); + + connect(poll_timer.get(), &QTimer::timeout, [this]() { + Common::ParamPackage params; + for (auto& poller : device_pollers) { + params = poller->GetNextInput(); + if (params.Has("engine")) { + SetPollingResult(params, false); + return; + } + } + }); +} + +void ConfigureTouchFromButton::SaveCurrentMapping() { + auto& map = touch_maps[selected_index]; + map.buttons.clear(); + for (int i = 0, rc = binding_list_model->rowCount(); i < rc; ++i) { + const auto bind_str = binding_list_model->index(i, 0) + .data(Qt::ItemDataRole::UserRole + 1) + .toString() + .toStdString(); + if (bind_str.empty()) { + continue; + } + Common::ParamPackage params{bind_str}; + if (!params.Has("engine")) { + continue; + } + params.Set("x", binding_list_model->index(i, 1).data().toInt()); + params.Set("y", binding_list_model->index(i, 2).data().toInt()); + map.buttons.emplace_back(params.Serialize()); + } +} + +void ConfigureTouchFromButton::NewMapping() { + const QString name = + QInputDialog::getText(this, tr("New Profile"), tr("Enter the name for the new profile.")); + if (name.isEmpty()) { + return; + } + touch_maps.emplace_back(Settings::TouchFromButtonMap{name.toStdString(), {}}); + ui->mapping->addItem(name); + ui->mapping->setCurrentIndex(ui->mapping->count() - 1); +} + +void ConfigureTouchFromButton::DeleteMapping() { + const auto answer = QMessageBox::question( + this, tr("Delete Profile"), tr("Delete profile %1?").arg(ui->mapping->currentText())); + if (answer != QMessageBox::Yes) { + return; + } + const bool blocked = ui->mapping->blockSignals(true); + ui->mapping->removeItem(selected_index); + ui->mapping->blockSignals(blocked); + touch_maps.erase(touch_maps.begin() + selected_index); + selected_index = ui->mapping->currentIndex(); + UpdateUiDisplay(); +} + +void ConfigureTouchFromButton::RenameMapping() { + const QString new_name = QInputDialog::getText(this, tr("Rename Profile"), tr("New name:")); + if (new_name.isEmpty()) { + return; + } + ui->mapping->setItemText(selected_index, new_name); + touch_maps[selected_index].name = new_name.toStdString(); +} + +void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is_new) { + binding_list_model->item(row_index, 0)->setText(tr("[press key]")); + + input_setter = [this, row_index, is_new](const Common::ParamPackage& params, + const bool cancel) { + auto* cell = binding_list_model->item(row_index, 0); + if (cancel) { + if (is_new) { + binding_list_model->removeRow(row_index); + } else { + cell->setText( + ButtonToText(Common::ParamPackage{cell->data().toString().toStdString()})); + } + } else { + cell->setText(ButtonToText(params)); + cell->setData(QString::fromStdString(params.Serialize())); + } + }; + + device_pollers = input_subsystem->GetPollers(InputCommon::Polling::DeviceType::Button); + + for (auto& poller : device_pollers) { + poller->Start(); + } + + grabKeyboard(); + grabMouse(); + qApp->setOverrideCursor(QCursor(Qt::CursorShape::ArrowCursor)); + timeout_timer->start(5000); // Cancel after 5 seconds + poll_timer->start(200); // Check for new inputs every 200ms +} + +void ConfigureTouchFromButton::NewBinding(const QPoint& pos) { + auto* button = new QStandardItem(); + button->setEditable(false); + auto* x_coord = new QStandardItem(QString::number(pos.x())); + auto* y_coord = new QStandardItem(QString::number(pos.y())); + + const int dot_id = ui->bottom_screen->AddDot(pos.x(), pos.y()); + button->setData(dot_id, DataRoleDot); + + binding_list_model->appendRow({button, x_coord, y_coord}); + ui->binding_list->setFocus(); + ui->binding_list->setCurrentIndex(button->index()); + + GetButtonInput(binding_list_model->rowCount() - 1, true); +} + +void ConfigureTouchFromButton::EditBinding(const QModelIndex& qi) { + if (qi.row() >= 0 && qi.column() == 0) { + GetButtonInput(qi.row(), false); + } +} + +void ConfigureTouchFromButton::DeleteBinding() { + const int row_index = ui->binding_list->currentIndex().row(); + if (row_index < 0) { + return; + } + ui->bottom_screen->RemoveDot(binding_list_model->index(row_index, 0).data(DataRoleDot).toInt()); + binding_list_model->removeRow(row_index); +} + +void ConfigureTouchFromButton::OnBindingSelection(const QItemSelection& selected, + const QItemSelection& deselected) { + ui->button_delete_bind->setEnabled(!selected.isEmpty()); + if (!selected.isEmpty()) { + const auto dot_data = selected.indexes().first().data(DataRoleDot); + if (dot_data.isValid()) { + ui->bottom_screen->HighlightDot(dot_data.toInt()); + } + } + if (!deselected.isEmpty()) { + const auto dot_data = deselected.indexes().first().data(DataRoleDot); + if (dot_data.isValid()) { + ui->bottom_screen->HighlightDot(dot_data.toInt(), false); + } + } +} + +void ConfigureTouchFromButton::OnBindingChanged(QStandardItem* item) { + if (item->column() == 0) { + return; + } + + const bool blocked = binding_list_model->blockSignals(true); + item->setText(QString::number( + std::clamp(item->text().toInt(), 0, + static_cast<int>((item->column() == 1 ? Layout::ScreenUndocked::Width + : Layout::ScreenUndocked::Height) - + 1)))); + binding_list_model->blockSignals(blocked); + + const auto dot_data = binding_list_model->index(item->row(), 0).data(DataRoleDot); + if (dot_data.isValid()) { + ui->bottom_screen->MoveDot(dot_data.toInt(), + binding_list_model->item(item->row(), 1)->text().toInt(), + binding_list_model->item(item->row(), 2)->text().toInt()); + } +} + +void ConfigureTouchFromButton::OnBindingDeleted(const QModelIndex& parent, int first, int last) { + for (int i = first; i <= last; ++i) { + const auto ix = binding_list_model->index(i, 0); + if (!ix.isValid()) { + return; + } + const auto dot_data = ix.data(DataRoleDot); + if (dot_data.isValid()) { + ui->bottom_screen->RemoveDot(dot_data.toInt()); + } + } +} + +void ConfigureTouchFromButton::SetActiveBinding(const int dot_id) { + for (int i = 0; i < binding_list_model->rowCount(); ++i) { + if (binding_list_model->index(i, 0).data(DataRoleDot) == dot_id) { + ui->binding_list->setCurrentIndex(binding_list_model->index(i, 0)); + ui->binding_list->setFocus(); + return; + } + } +} + +void ConfigureTouchFromButton::SetCoordinates(const int dot_id, const QPoint& pos) { + for (int i = 0; i < binding_list_model->rowCount(); ++i) { + if (binding_list_model->item(i, 0)->data(DataRoleDot) == dot_id) { + binding_list_model->item(i, 1)->setText(QString::number(pos.x())); + binding_list_model->item(i, 2)->setText(QString::number(pos.y())); + return; + } + } +} + +void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params, + const bool cancel) { + releaseKeyboard(); + releaseMouse(); + qApp->restoreOverrideCursor(); + timeout_timer->stop(); + poll_timer->stop(); + for (auto& poller : device_pollers) { + poller->Stop(); + } + if (input_setter) { + (*input_setter)(params, cancel); + input_setter.reset(); + } +} + +void ConfigureTouchFromButton::keyPressEvent(QKeyEvent* event) { + if (!input_setter && event->key() == Qt::Key_Delete) { + DeleteBinding(); + return; + } + + if (!input_setter) { + return QDialog::keyPressEvent(event); + } + + if (event->key() != Qt::Key_Escape) { + SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, + false); + } else { + SetPollingResult({}, true); + } +} + +void ConfigureTouchFromButton::ApplyConfiguration() { + SaveCurrentMapping(); + accept(); +} + +int ConfigureTouchFromButton::GetSelectedIndex() const { + return selected_index; +} + +std::vector<Settings::TouchFromButtonMap> ConfigureTouchFromButton::GetMaps() const { + return touch_maps; +} + +TouchScreenPreview::TouchScreenPreview(QWidget* parent) : QFrame(parent) { + setBackgroundRole(QPalette::ColorRole::Base); +} + +TouchScreenPreview::~TouchScreenPreview() = default; + +void TouchScreenPreview::SetCoordLabel(QLabel* const label) { + coord_label = label; +} + +int TouchScreenPreview::AddDot(const int device_x, const int device_y) { + QFont dot_font{QStringLiteral("monospace")}; + dot_font.setStyleHint(QFont::Monospace); + dot_font.setPointSize(20); + + auto* dot = new QLabel(this); + dot->setAttribute(Qt::WA_TranslucentBackground); + dot->setFont(dot_font); + dot->setText(QChar(0xD7)); // U+00D7 Multiplication Sign + dot->setAlignment(Qt::AlignmentFlag::AlignCenter); + dot->setProperty(PropId, ++max_dot_id); + dot->setProperty(PropX, device_x); + dot->setProperty(PropY, device_y); + dot->setCursor(Qt::CursorShape::PointingHandCursor); + dot->setMouseTracking(true); + dot->installEventFilter(this); + dot->show(); + PositionDot(dot, device_x, device_y); + dots.emplace_back(max_dot_id, dot); + return max_dot_id; +} + +void TouchScreenPreview::RemoveDot(const int id) { + const auto iter = std::find_if(dots.begin(), dots.end(), + [id](const auto& entry) { return entry.first == id; }); + if (iter == dots.cend()) { + return; + } + + iter->second->deleteLater(); + dots.erase(iter); +} + +void TouchScreenPreview::HighlightDot(const int id, const bool active) const { + for (const auto& dot : dots) { + if (dot.first == id) { + // use color property from the stylesheet, or fall back to the default palette + if (dot_highlight_color.isValid()) { + dot.second->setStyleSheet( + active ? QStringLiteral("color: %1").arg(dot_highlight_color.name()) + : QString{}); + } else { + dot.second->setForegroundRole(active ? QPalette::ColorRole::LinkVisited + : QPalette::ColorRole::NoRole); + } + if (active) { + dot.second->raise(); + } + return; + } + } +} + +void TouchScreenPreview::MoveDot(const int id, const int device_x, const int device_y) const { + const auto iter = std::find_if(dots.begin(), dots.end(), + [id](const auto& entry) { return entry.first == id; }); + if (iter == dots.cend()) { + return; + } + + iter->second->setProperty(PropX, device_x); + iter->second->setProperty(PropY, device_y); + PositionDot(iter->second, device_x, device_y); +} + +void TouchScreenPreview::resizeEvent(QResizeEvent* event) { + if (ignore_resize) { + return; + } + + const int target_width = std::min(width(), height() * 4 / 3); + const int target_height = std::min(height(), width() * 3 / 4); + if (target_width == width() && target_height == height()) { + return; + } + ignore_resize = true; + setGeometry((parentWidget()->contentsRect().width() - target_width) / 2, y(), target_width, + target_height); + ignore_resize = false; + + if (event->oldSize().width() != target_width || event->oldSize().height() != target_height) { + for (const auto& dot : dots) { + PositionDot(dot.second); + } + } +} + +void TouchScreenPreview::mouseMoveEvent(QMouseEvent* event) { + if (!coord_label) { + return; + } + const auto pos = MapToDeviceCoords(event->x(), event->y()); + if (pos) { + coord_label->setText(QStringLiteral("X: %1, Y: %2").arg(pos->x()).arg(pos->y())); + } else { + coord_label->clear(); + } +} + +void TouchScreenPreview::leaveEvent(QEvent* event) { + if (coord_label) { + coord_label->clear(); + } +} + +void TouchScreenPreview::mousePressEvent(QMouseEvent* event) { + if (event->button() != Qt::MouseButton::LeftButton) { + return; + } + const auto pos = MapToDeviceCoords(event->x(), event->y()); + if (pos) { + emit DotAdded(*pos); + } +} + +bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) { + switch (event->type()) { + case QEvent::Type::MouseButtonPress: { + const auto mouse_event = static_cast<QMouseEvent*>(event); + if (mouse_event->button() != Qt::MouseButton::LeftButton) { + break; + } + emit DotSelected(obj->property(PropId).toInt()); + + drag_state.dot = qobject_cast<QLabel*>(obj); + drag_state.start_pos = mouse_event->globalPos(); + return true; + } + case QEvent::Type::MouseMove: { + if (!drag_state.dot) { + break; + } + const auto mouse_event = static_cast<QMouseEvent*>(event); + if (!drag_state.active) { + drag_state.active = + (mouse_event->globalPos() - drag_state.start_pos).manhattanLength() >= + QApplication::startDragDistance(); + if (!drag_state.active) { + break; + } + } + auto current_pos = mapFromGlobal(mouse_event->globalPos()); + current_pos.setX(std::clamp(current_pos.x(), contentsMargins().left(), + contentsMargins().left() + contentsRect().width() - 1)); + current_pos.setY(std::clamp(current_pos.y(), contentsMargins().top(), + contentsMargins().top() + contentsRect().height() - 1)); + const auto device_coord = MapToDeviceCoords(current_pos.x(), current_pos.y()); + if (device_coord) { + drag_state.dot->setProperty(PropX, device_coord->x()); + drag_state.dot->setProperty(PropY, device_coord->y()); + PositionDot(drag_state.dot, device_coord->x(), device_coord->y()); + emit DotMoved(drag_state.dot->property(PropId).toInt(), *device_coord); + if (coord_label) { + coord_label->setText( + QStringLiteral("X: %1, Y: %2").arg(device_coord->x()).arg(device_coord->y())); + } + } + return true; + } + case QEvent::Type::MouseButtonRelease: { + drag_state.dot.clear(); + drag_state.active = false; + return true; + } + default: + break; + } + return obj->eventFilter(obj, event); +} + +std::optional<QPoint> TouchScreenPreview::MapToDeviceCoords(const int screen_x, + const int screen_y) const { + const float t_x = 0.5f + static_cast<float>(screen_x - contentsMargins().left()) * + (Layout::ScreenUndocked::Width - 1) / (contentsRect().width() - 1); + const float t_y = 0.5f + static_cast<float>(screen_y - contentsMargins().top()) * + (Layout::ScreenUndocked::Height - 1) / + (contentsRect().height() - 1); + if (t_x >= 0.5f && t_x < Layout::ScreenUndocked::Width && t_y >= 0.5f && + t_y < Layout::ScreenUndocked::Height) { + + return QPoint{static_cast<int>(t_x), static_cast<int>(t_y)}; + } + return std::nullopt; +} + +void TouchScreenPreview::PositionDot(QLabel* const dot, const int device_x, + const int device_y) const { + const float device_coord_x = + static_cast<float>(device_x >= 0 ? device_x : dot->property(PropX).toInt()); + int x_coord = static_cast<int>( + device_coord_x * (contentsRect().width() - 1) / (Layout::ScreenUndocked::Width - 1) + + contentsMargins().left() - static_cast<float>(dot->width()) / 2 + 0.5f); + + const float device_coord_y = + static_cast<float>(device_y >= 0 ? device_y : dot->property(PropY).toInt()); + const int y_coord = static_cast<int>( + device_coord_y * (contentsRect().height() - 1) / (Layout::ScreenUndocked::Height - 1) + + contentsMargins().top() - static_cast<float>(dot->height()) / 2 + 0.5f); + + dot->move(x_coord, y_coord); +} diff --git a/src/yuzu/configuration/configure_touch_from_button.h b/src/yuzu/configuration/configure_touch_from_button.h new file mode 100644 index 000000000..d9513e3bc --- /dev/null +++ b/src/yuzu/configuration/configure_touch_from_button.h @@ -0,0 +1,92 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <functional> +#include <memory> +#include <optional> +#include <vector> +#include <QDialog> + +class QItemSelection; +class QModelIndex; +class QStandardItemModel; +class QStandardItem; +class QTimer; + +namespace Common { +class ParamPackage; +} + +namespace InputCommon { +class InputSubsystem; +} + +namespace InputCommon::Polling { +class DevicePoller; +} + +namespace Settings { +struct TouchFromButtonMap; +} + +namespace Ui { +class ConfigureTouchFromButton; +} + +class ConfigureTouchFromButton : public QDialog { + Q_OBJECT + +public: + explicit ConfigureTouchFromButton(QWidget* parent, + const std::vector<Settings::TouchFromButtonMap>& touch_maps, + InputCommon::InputSubsystem* input_subsystem_, + int default_index = 0); + ~ConfigureTouchFromButton() override; + + int GetSelectedIndex() const; + std::vector<Settings::TouchFromButtonMap> GetMaps() const; + +public slots: + void ApplyConfiguration(); + void NewBinding(const QPoint& pos); + void SetActiveBinding(int dot_id); + void SetCoordinates(int dot_id, const QPoint& pos); + +protected: + void showEvent(QShowEvent* ev) override; + void keyPressEvent(QKeyEvent* event) override; + +private slots: + void NewMapping(); + void DeleteMapping(); + void RenameMapping(); + void EditBinding(const QModelIndex& qi); + void DeleteBinding(); + void OnBindingSelection(const QItemSelection& selected, const QItemSelection& deselected); + void OnBindingChanged(QStandardItem* item); + void OnBindingDeleted(const QModelIndex& parent, int first, int last); + +private: + void SetConfiguration(); + void UpdateUiDisplay(); + void ConnectEvents(); + void GetButtonInput(int row_index, bool is_new); + void SetPollingResult(const Common::ParamPackage& params, bool cancel); + void SaveCurrentMapping(); + + std::unique_ptr<Ui::ConfigureTouchFromButton> ui; + std::vector<Settings::TouchFromButtonMap> touch_maps; + QStandardItemModel* binding_list_model; + InputCommon::InputSubsystem* input_subsystem; + int selected_index; + + std::unique_ptr<QTimer> timeout_timer; + std::unique_ptr<QTimer> poll_timer; + std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; + std::optional<std::function<void(const Common::ParamPackage&, bool)>> input_setter; + + static constexpr int DataRoleDot = Qt::ItemDataRole::UserRole + 2; +}; diff --git a/src/yuzu/configuration/configure_touch_from_button.ui b/src/yuzu/configuration/configure_touch_from_button.ui new file mode 100644 index 000000000..757219d54 --- /dev/null +++ b/src/yuzu/configuration/configure_touch_from_button.ui @@ -0,0 +1,221 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureTouchFromButton</class> + <widget class="QDialog" name="ConfigureTouchFromButton"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>500</width> + <height>500</height> + </rect> + </property> + <property name="windowTitle"> + <string>Configure Touchscreen Mappings</string> + </property> + <layout class="QVBoxLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Mapping:</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="mapping"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="button_new"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>New</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="button_delete"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Delete</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="button_rename"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Rename</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Click the bottom area to add a point, then press a button to bind. +Drag points to change position, or double-click table cells to edit values.</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </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="button_delete_bind"> + <property name="text"> + <string>Delete Point</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QTreeView" name="binding_list"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="uniformRowHeights"> + <bool>true</bool> + </property> + <property name="itemsExpandable"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="TouchScreenPreview" name="bottom_screen"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>160</width> + <height>120</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>320</width> + <height>240</height> + </size> + </property> + <property name="cursor"> + <cursorShape>CrossCursor</cursorShape> + </property> + <property name="mouseTracking"> + <bool>true</bool> + </property> + <property name="autoFillBackground"> + <bool>true</bool> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Sunken</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="coord_label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </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> + <customwidgets> + <customwidget> + <class>TouchScreenPreview</class> + <extends>QFrame</extends> + <header>yuzu/configuration/configure_touch_widget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ConfigureTouchFromButton</receiver> + <slot>reject()</slot> + </connection> + </connections> +</ui> diff --git a/src/yuzu/configuration/configure_touch_widget.h b/src/yuzu/configuration/configure_touch_widget.h new file mode 100644 index 000000000..347b46583 --- /dev/null +++ b/src/yuzu/configuration/configure_touch_widget.h @@ -0,0 +1,62 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <optional> +#include <utility> +#include <vector> +#include <QFrame> +#include <QPointer> + +class QLabel; + +// Widget for representing touchscreen coordinates +class TouchScreenPreview : public QFrame { + Q_OBJECT + Q_PROPERTY(QColor dotHighlightColor MEMBER dot_highlight_color) + +public: + explicit TouchScreenPreview(QWidget* parent); + ~TouchScreenPreview() override; + + void SetCoordLabel(QLabel*); + int AddDot(int device_x, int device_y); + void RemoveDot(int id); + void HighlightDot(int id, bool active = true) const; + void MoveDot(int id, int device_x, int device_y) const; + +signals: + void DotAdded(const QPoint& pos); + void DotSelected(int dot_id); + void DotMoved(int dot_id, const QPoint& pos); + +protected: + void resizeEvent(QResizeEvent*) override; + void mouseMoveEvent(QMouseEvent*) override; + void leaveEvent(QEvent*) override; + void mousePressEvent(QMouseEvent*) override; + bool eventFilter(QObject*, QEvent*) override; + +private: + std::optional<QPoint> MapToDeviceCoords(int screen_x, int screen_y) const; + void PositionDot(QLabel* dot, int device_x = -1, int device_y = -1) const; + + bool ignore_resize = false; + QPointer<QLabel> coord_label; + + std::vector<std::pair<int, QLabel*>> dots; + int max_dot_id = 0; + QColor dot_highlight_color; + static constexpr char PropId[] = "dot_id"; + static constexpr char PropX[] = "device_x"; + static constexpr char PropY[] = "device_y"; + + struct DragState { + bool active = false; + QPointer<QLabel> dot; + QPoint start_pos; + }; + DragState drag_state; +}; diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui index 1171c2dd1..30ceccddb 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.ui +++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui @@ -168,32 +168,12 @@ <signal>accepted()</signal> <receiver>ConfigureTouchscreenAdvanced</receiver> <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>140</x> - <y>318</y> - </hint> - <hint type="destinationlabel"> - <x>140</x> - <y>169</y> - </hint> - </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>ConfigureTouchscreenAdvanced</receiver> <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>140</x> - <y>318</y> - </hint> - <hint type="destinationlabel"> - <x>140</x> - <y>169</y> - </hint> - </hints> - </connection> + </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp new file mode 100644 index 000000000..7dcb2c5b9 --- /dev/null +++ b/src/yuzu/configuration/configure_vibration.cpp @@ -0,0 +1,146 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <unordered_map> + +#include <fmt/format.h> + +#include "common/param_package.h" +#include "core/settings.h" +#include "ui_configure_vibration.h" +#include "yuzu/configuration/configure_vibration.h" + +ConfigureVibration::ConfigureVibration(QWidget* parent) + : QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()) { + ui->setupUi(this); + + vibration_groupboxes = { + ui->vibrationGroupPlayer1, ui->vibrationGroupPlayer2, ui->vibrationGroupPlayer3, + ui->vibrationGroupPlayer4, ui->vibrationGroupPlayer5, ui->vibrationGroupPlayer6, + ui->vibrationGroupPlayer7, ui->vibrationGroupPlayer8, + }; + + vibration_spinboxes = { + ui->vibrationSpinPlayer1, ui->vibrationSpinPlayer2, ui->vibrationSpinPlayer3, + ui->vibrationSpinPlayer4, ui->vibrationSpinPlayer5, ui->vibrationSpinPlayer6, + ui->vibrationSpinPlayer7, ui->vibrationSpinPlayer8, + }; + + const auto& players = Settings::values.players.GetValue(); + + for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { + vibration_groupboxes[i]->setChecked(players[i].vibration_enabled); + vibration_spinboxes[i]->setValue(players[i].vibration_strength); + } + + ui->checkBoxAccurateVibration->setChecked( + Settings::values.enable_accurate_vibrations.GetValue()); + + if (!Settings::IsConfiguringGlobal()) { + ui->checkBoxAccurateVibration->setDisabled(true); + } + + RetranslateUI(); +} + +ConfigureVibration::~ConfigureVibration() = default; + +void ConfigureVibration::ApplyConfiguration() { + auto& players = Settings::values.players.GetValue(); + + for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { + players[i].vibration_enabled = vibration_groupboxes[i]->isChecked(); + players[i].vibration_strength = vibration_spinboxes[i]->value(); + } + + Settings::values.enable_accurate_vibrations.SetValue( + ui->checkBoxAccurateVibration->isChecked()); +} + +void ConfigureVibration::SetVibrationDevices(std::size_t player_index) { + using namespace Settings::NativeButton; + static constexpr std::array<std::array<Settings::NativeButton::Values, 6>, 2> buttons{{ + {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons + {A, B, X, Y, R, ZR}, // Right Buttons + }}; + + auto& player = Settings::values.players.GetValue()[player_index]; + + for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) { + std::unordered_map<std::string, int> params_count; + + for (const auto button_index : buttons[device_idx]) { + const auto& player_button = player.buttons[button_index]; + + if (params_count.find(player_button) != params_count.end()) { + ++params_count[player_button]; + continue; + } + + params_count.insert_or_assign(player_button, 1); + } + + const auto it = std::max_element( + params_count.begin(), params_count.end(), + [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; }); + + auto& vibration_param_str = player.vibrations[device_idx]; + vibration_param_str.clear(); + + if (it->first.empty()) { + continue; + } + + const auto param = Common::ParamPackage(it->first); + + const auto engine = param.Get("engine", ""); + const auto guid = param.Get("guid", ""); + const auto port = param.Get("port", ""); + + if (engine.empty() || engine == "keyboard" || engine == "mouse") { + continue; + } + + vibration_param_str += fmt::format("engine:{}", engine); + + if (!port.empty()) { + vibration_param_str += fmt::format(",port:{}", port); + } + if (!guid.empty()) { + vibration_param_str += fmt::format(",guid:{}", guid); + } + } + + if (player.vibrations[0] != player.vibrations[1]) { + return; + } + + if (!player.vibrations[0].empty() && + player.controller_type != Settings::ControllerType::RightJoycon) { + player.vibrations[1].clear(); + } else if (!player.vibrations[1].empty() && + player.controller_type == Settings::ControllerType::RightJoycon) { + player.vibrations[0].clear(); + } +} + +void ConfigureVibration::SetAllVibrationDevices() { + // Set vibration devices for all player indices including handheld + for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) { + SetVibrationDevices(player_idx); + } +} + +void ConfigureVibration::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QDialog::changeEvent(event); +} + +void ConfigureVibration::RetranslateUI() { + ui->retranslateUi(this); +} diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h new file mode 100644 index 000000000..07411a86f --- /dev/null +++ b/src/yuzu/configuration/configure_vibration.h @@ -0,0 +1,43 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <memory> +#include <QDialog> + +class QGroupBox; +class QSpinBox; + +namespace Ui { +class ConfigureVibration; +} + +class ConfigureVibration : public QDialog { + Q_OBJECT + +public: + explicit ConfigureVibration(QWidget* parent); + ~ConfigureVibration() override; + + void ApplyConfiguration(); + + static void SetVibrationDevices(std::size_t player_index); + static void SetAllVibrationDevices(); + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + std::unique_ptr<Ui::ConfigureVibration> ui; + + static constexpr std::size_t NUM_PLAYERS = 8; + + // Groupboxes encapsulating the vibration strength spinbox. + std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes; + + // Spinboxes representing the vibration strength percentage. + std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes; +}; diff --git a/src/yuzu/configuration/configure_vibration.ui b/src/yuzu/configuration/configure_vibration.ui new file mode 100644 index 000000000..efdf317a9 --- /dev/null +++ b/src/yuzu/configuration/configure_vibration.ui @@ -0,0 +1,546 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureVibration</class> + <widget class="QDialog" name="ConfigureVibration"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>364</width> + <height>242</height> + </rect> + </property> + <property name="windowTitle"> + <string>Configure Vibration</string> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="QGroupBox" name="vibrationStrengthGroup"> + <property name="title"> + <string>Vibration</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,0"> + <property name="leftMargin"> + <number>9</number> + </property> + <property name="topMargin"> + <number>9</number> + </property> + <property name="rightMargin"> + <number>9</number> + </property> + <property name="bottomMargin"> + <number>9</number> + </property> + <item> + <widget class="QWidget" name="player14Widget" 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="QGroupBox" name="vibrationGroupPlayer1"> + <property name="title"> + <string>Player 1</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer1"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer2"> + <property name="title"> + <string>Player 2</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_9"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer2"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer3"> + <property name="title"> + <string>Player 3</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_10"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer3"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer4"> + <property name="title"> + <string>Player 4</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_11"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer4"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player58Widget" 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="QGroupBox" name="vibrationGroupPlayer7"> + <property name="title"> + <string>Player 5</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_14"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer7"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer8"> + <property name="title"> + <string>Player 6</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_15"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer8"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer5"> + <property name="title"> + <string>Player 7</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_12"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer5"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationGroupPlayer6"> + <property name="title"> + <string>Player 8</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_13"> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>3</number> + </property> + <property name="rightMargin"> + <number>3</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="QSpinBox" name="vibrationSpinPlayer6"> + <property name="minimumSize"> + <size> + <width>68</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>68</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>150</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="vibrationSettingsGroup"> + <property name="title"> + <string>Settings</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="checkBoxAccurateVibration"> + <property name="text"> + <string>Enable Accurate Vibration</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="spacerVibration"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>167</width> + <height>55</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBoxVibration"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBoxVibration</sender> + <signal>accepted()</signal> + <receiver>ConfigureVibration</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>buttonBoxVibration</sender> + <signal>rejected()</signal> + <receiver>ConfigureVibration</receiver> + <slot>reject()</slot> + </connection> + </connections> +</ui> diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp new file mode 100644 index 000000000..e87aededb --- /dev/null +++ b/src/yuzu/configuration/input_profiles.cpp @@ -0,0 +1,131 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <fmt/format.h> + +#include "common/common_paths.h" +#include "common/file_util.h" +#include "yuzu/configuration/config.h" +#include "yuzu/configuration/input_profiles.h" + +namespace FS = Common::FS; + +namespace { + +bool ProfileExistsInFilesystem(std::string_view profile_name) { + return FS::Exists(fmt::format("{}input" DIR_SEP "{}.ini", + FS::GetUserPath(FS::UserPath::ConfigDir), profile_name)); +} + +bool IsINI(std::string_view filename) { + const std::size_t index = filename.rfind('.'); + + if (index == std::string::npos) { + return false; + } + + return filename.substr(index) == ".ini"; +} + +std::string GetNameWithoutExtension(const std::string& filename) { + const std::size_t index = filename.rfind('.'); + + if (index == std::string::npos) { + return filename; + } + + return filename.substr(0, index); +} + +} // namespace + +InputProfiles::InputProfiles() { + const std::string input_profile_loc = + fmt::format("{}input", FS::GetUserPath(FS::UserPath::ConfigDir)); + + FS::ForeachDirectoryEntry( + nullptr, input_profile_loc, + [this](u64* entries_out, const std::string& directory, const std::string& filename) { + if (IsINI(filename) && IsProfileNameValid(GetNameWithoutExtension(filename))) { + map_profiles.insert_or_assign( + GetNameWithoutExtension(filename), + std::make_unique<Config>(GetNameWithoutExtension(filename), + Config::ConfigType::InputProfile)); + } + return true; + }); +} + +InputProfiles::~InputProfiles() = default; + +std::vector<std::string> InputProfiles::GetInputProfileNames() { + std::vector<std::string> profile_names; + profile_names.reserve(map_profiles.size()); + + for (const auto& [profile_name, config] : map_profiles) { + if (!ProfileExistsInFilesystem(profile_name)) { + DeleteProfile(profile_name); + continue; + } + + profile_names.push_back(profile_name); + } + + return profile_names; +} + +bool InputProfiles::IsProfileNameValid(std::string_view profile_name) { + return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos; +} + +bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t player_index) { + if (ProfileExistsInMap(profile_name)) { + return false; + } + + map_profiles.insert_or_assign( + profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile)); + + return SaveProfile(profile_name, player_index); +} + +bool InputProfiles::DeleteProfile(const std::string& profile_name) { + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + if (!ProfileExistsInFilesystem(profile_name) || + FS::Delete(map_profiles[profile_name]->GetConfigFilePath())) { + map_profiles.erase(profile_name); + } + + return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name); +} + +bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t player_index) { + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + if (!ProfileExistsInFilesystem(profile_name)) { + map_profiles.erase(profile_name); + return false; + } + + map_profiles[profile_name]->ReadControlPlayerValue(player_index); + return true; +} + +bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t player_index) { + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + map_profiles[profile_name]->SaveControlPlayerValue(player_index); + return true; +} + +bool InputProfiles::ProfileExistsInMap(const std::string& profile_name) const { + return map_profiles.find(profile_name) != map_profiles.end(); +} diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h new file mode 100644 index 000000000..cb41fd9be --- /dev/null +++ b/src/yuzu/configuration/input_profiles.h @@ -0,0 +1,32 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <string_view> +#include <unordered_map> + +class Config; + +class InputProfiles { + +public: + explicit InputProfiles(); + virtual ~InputProfiles(); + + std::vector<std::string> GetInputProfileNames(); + + static bool IsProfileNameValid(std::string_view profile_name); + + bool CreateProfile(const std::string& profile_name, std::size_t player_index); + bool DeleteProfile(const std::string& profile_name); + bool LoadProfile(const std::string& profile_name, std::size_t player_index); + bool SaveProfile(const std::string& profile_name, std::size_t player_index); + +private: + bool ProfileExistsInMap(const std::string& profile_name) const; + + std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles; +}; |
