diff options
author | Morph <39850852+Morph1984@users.noreply.github.com> | 2020-07-22 16:39:53 +0200 |
---|---|---|
committer | Morph <39850852+Morph1984@users.noreply.github.com> | 2020-08-26 08:32:32 +0200 |
commit | f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01 (patch) | |
tree | 4438688a9b9b4bc015985f2df1a731de57fe50db /src | |
parent | Merge pull request #4582 from lioncash/xbyak (diff) | |
download | yuzu-f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01.tar yuzu-f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01.tar.gz yuzu-f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01.tar.bz2 yuzu-f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01.tar.lz yuzu-f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01.tar.xz yuzu-f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01.tar.zst yuzu-f0fac0c7fb6f7dd9fe81747b3369767c8c9e7d01.zip |
Diffstat (limited to '')
43 files changed, 8306 insertions, 3177 deletions
diff --git a/src/common/param_package.h b/src/common/param_package.h index c8a70bfa9..c13e45479 100644 --- a/src/common/param_package.h +++ b/src/common/param_package.h @@ -19,7 +19,7 @@ public: explicit ParamPackage(const std::string& serialized); ParamPackage(std::initializer_list<DataType::value_type> list); ParamPackage(const ParamPackage& other) = default; - ParamPackage(ParamPackage&& other) = default; + ParamPackage(ParamPackage&& other) noexcept = default; ParamPackage& operator=(const ParamPackage& other) = default; ParamPackage& operator=(ParamPackage&& other) = default; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 0e7794dc7..82e11b169 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff; constexpr std::size_t NPAD_OFFSET = 0x9A00; constexpr u32 BATTERY_FULL = 2; constexpr u32 MAX_NPAD_ID = 7; +constexpr std::size_t HANDHELD_INDEX = 8; constexpr std::array<u32, 10> npad_id_list{ 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, }; @@ -33,19 +34,41 @@ enum class JoystickId : std::size_t { Joystick_Right, }; -static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) { +Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad( + Settings::ControllerType type) { switch (type) { case Settings::ControllerType::ProController: - return Controller_NPad::NPadControllerType::ProController; - case Settings::ControllerType::DualJoycon: - return Controller_NPad::NPadControllerType::JoyDual; + return NPadControllerType::ProController; + case Settings::ControllerType::DualJoyconDetached: + return NPadControllerType::JoyDual; case Settings::ControllerType::LeftJoycon: - return Controller_NPad::NPadControllerType::JoyLeft; + return NPadControllerType::JoyLeft; case Settings::ControllerType::RightJoycon: - return Controller_NPad::NPadControllerType::JoyRight; + return NPadControllerType::JoyRight; + case Settings::ControllerType::Handheld: + return NPadControllerType::Handheld; default: UNREACHABLE(); - return Controller_NPad::NPadControllerType::JoyDual; + return NPadControllerType::ProController; + } +} + +Settings::ControllerType Controller_NPad::MapNPadToSettingsType( + Controller_NPad::NPadControllerType type) { + switch (type) { + case NPadControllerType::ProController: + return Settings::ControllerType::ProController; + case NPadControllerType::JoyDual: + return Settings::ControllerType::DualJoyconDetached; + case NPadControllerType::JoyLeft: + return Settings::ControllerType::LeftJoycon; + case NPadControllerType::JoyRight: + return Settings::ControllerType::RightJoycon; + case NPadControllerType::Handheld: + return Settings::ControllerType::Handheld; + default: + UNREACHABLE(); + return Settings::ControllerType::ProController; } } @@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) { case 6: case 7: return npad_id; - case 8: + case HANDHELD_INDEX: case NPAD_HANDHELD: - return 8; + return HANDHELD_INDEX; case 9: case NPAD_UNKNOWN: return 9; @@ -83,7 +106,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) { case 6: case 7: return static_cast<u32>(index); - case 8: + case HANDHELD_INDEX: return NPAD_HANDHELD; case 9: return NPAD_UNKNOWN; @@ -96,25 +119,35 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) { Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} Controller_NPad::~Controller_NPad() = default; -void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { +void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { const auto controller_type = connected_controllers[controller_idx].type; auto& controller = shared_memory_entries[controller_idx]; if (controller_type == NPadControllerType::None) { + styleset_changed_events[controller_idx].writable->Signal(); return; } controller.joy_styles.raw = 0; // Zero out controller.device_type.raw = 0; + controller.properties.raw = 0; switch (controller_type) { case NPadControllerType::None: UNREACHABLE(); break; + case NPadControllerType::ProController: + controller.joy_styles.pro_controller.Assign(1); + controller.device_type.pro_controller.Assign(1); + controller.properties.is_vertical.Assign(1); + controller.properties.use_plus.Assign(1); + controller.properties.use_minus.Assign(1); + controller.pad_assignment = NPadAssignments::Single; + break; case NPadControllerType::Handheld: controller.joy_styles.handheld.Assign(1); controller.device_type.handheld.Assign(1); - controller.pad_assignment = NPadAssignments::Dual; controller.properties.is_vertical.Assign(1); controller.properties.use_plus.Assign(1); controller.properties.use_minus.Assign(1); + controller.pad_assignment = NPadAssignments::Dual; break; case NPadControllerType::JoyDual: controller.joy_styles.joycon_dual.Assign(1); @@ -144,14 +177,6 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { controller.device_type.pokeball.Assign(1); controller.pad_assignment = NPadAssignments::Single; break; - case NPadControllerType::ProController: - controller.joy_styles.pro_controller.Assign(1); - controller.device_type.pro_controller.Assign(1); - controller.properties.is_vertical.Assign(1); - controller.properties.use_plus.Assign(1); - controller.properties.use_minus.Assign(1); - controller.pad_assignment = NPadAssignments::Single; - break; } controller.single_color_error = ColorReadError::ReadOk; @@ -192,36 +217,25 @@ void Controller_NPad::OnInit() { style.pokeball.Assign(1); } - std::transform( - Settings::values.players.begin(), Settings::values.players.end(), - connected_controllers.begin(), [](const Settings::PlayerInput& player) { - return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected}; - }); - - std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8, - [](const ControllerHolder& holder) { return holder.is_connected; }); + std::transform(Settings::values.players.begin(), Settings::values.players.end(), + connected_controllers.begin(), [](const Settings::PlayerInput& player) { + return ControllerHolder{MapSettingsTypeToNPad(player.controller_type), + player.connected}; + }); // Account for handheld - if (connected_controllers[8].is_connected) - connected_controllers[8].type = NPadControllerType::Handheld; + if (connected_controllers[HANDHELD_INDEX].is_connected) { + connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld; + } supported_npad_id_types.resize(npad_id_list.size()); std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), npad_id_list.size() * sizeof(u32)); - // Add a default dual joycon controller if none are present. - if (std::none_of(connected_controllers.begin(), connected_controllers.end(), - [](const ControllerHolder& controller) { return controller.is_connected; })) { - supported_npad_id_types.resize(npad_id_list.size()); - std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), - npad_id_list.size() * sizeof(u32)); - AddNewController(NPadControllerType::JoyDual); - } - for (std::size_t i = 0; i < connected_controllers.size(); ++i) { const auto& controller = connected_controllers[i]; if (controller.is_connected) { - AddNewControllerAt(controller.type, IndexToNPad(i)); + AddNewControllerAt(controller.type, i); } } } @@ -309,8 +323,9 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t data_len) { - if (!IsControllerActivated()) + if (!IsControllerActivated()) { return; + } for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { auto& npad = shared_memory_entries[i]; const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, @@ -365,6 +380,14 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* case NPadControllerType::None: UNREACHABLE(); break; + case NPadControllerType::ProController: + main_controller.connection_status.raw = 0; + main_controller.connection_status.IsConnected.Assign(1); + main_controller.connection_status.IsWired.Assign(1); + main_controller.pad.pad_states.raw = pad_state.pad_states.raw; + main_controller.pad.l_stick = pad_state.l_stick; + main_controller.pad.r_stick = pad_state.r_stick; + break; case NPadControllerType::Handheld: handheld_entry.connection_status.raw = 0; handheld_entry.connection_status.IsWired.Assign(1); @@ -378,22 +401,19 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* break; case NPadControllerType::JoyDual: dual_entry.connection_status.raw = 0; - + dual_entry.connection_status.IsConnected.Assign(1); dual_entry.connection_status.IsLeftJoyConnected.Assign(1); dual_entry.connection_status.IsRightJoyConnected.Assign(1); - dual_entry.connection_status.IsConnected.Assign(1); - - libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); - libnx_entry.connection_status.IsRightJoyConnected.Assign(1); - libnx_entry.connection_status.IsConnected.Assign(1); - dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; dual_entry.pad.l_stick = pad_state.l_stick; dual_entry.pad.r_stick = pad_state.r_stick; + + libnx_entry.connection_status.IsConnected.Assign(1); + libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); + libnx_entry.connection_status.IsRightJoyConnected.Assign(1); break; case NPadControllerType::JoyLeft: left_entry.connection_status.raw = 0; - left_entry.connection_status.IsConnected.Assign(1); left_entry.pad.pad_states.raw = pad_state.pad_states.raw; left_entry.pad.l_stick = pad_state.l_stick; @@ -401,7 +421,6 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* break; case NPadControllerType::JoyRight: right_entry.connection_status.raw = 0; - right_entry.connection_status.IsConnected.Assign(1); right_entry.pad.pad_states.raw = pad_state.pad_states.raw; right_entry.pad.l_stick = pad_state.l_stick; @@ -409,23 +428,12 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* break; case NPadControllerType::Pokeball: pokeball_entry.connection_status.raw = 0; - pokeball_entry.connection_status.IsConnected.Assign(1); pokeball_entry.connection_status.IsWired.Assign(1); - pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; pokeball_entry.pad.l_stick = pad_state.l_stick; pokeball_entry.pad.r_stick = pad_state.r_stick; break; - case NPadControllerType::ProController: - main_controller.connection_status.raw = 0; - - main_controller.connection_status.IsConnected.Assign(1); - main_controller.connection_status.IsWired.Assign(1); - main_controller.pad.pad_states.raw = pad_state.pad_states.raw; - main_controller.pad.l_stick = pad_state.l_stick; - main_controller.pad.r_stick = pad_state.r_stick; - break; } // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate @@ -453,26 +461,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { supported_npad_id_types.clear(); supported_npad_id_types.resize(length / sizeof(u32)); std::memcpy(supported_npad_id_types.data(), data, length); - for (std::size_t i = 0; i < connected_controllers.size(); i++) { - auto& controller = connected_controllers[i]; - if (!controller.is_connected) { - continue; - } - const auto requested_controller = - i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type) - : NPadControllerType::Handheld; - if (!IsControllerSupported(requested_controller)) { - const auto is_handheld = requested_controller == NPadControllerType::Handheld; - if (is_handheld) { - controller.type = NPadControllerType::None; - controller.is_connected = false; - AddNewController(requested_controller); - } else { - controller.type = requested_controller; - InitNewlyAddedControler(i); - } - } - } } void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { @@ -504,7 +492,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, const std::vector<Vibration>& vibrations) { LOG_DEBUG(Service_HID, "(STUBBED) called"); - if (!can_controllers_vibrate) { + if (!Settings::values.vibration_enabled || !can_controllers_vibrate) { return; } for (std::size_t i = 0; i < controller_ids.size(); i++) { @@ -517,8 +505,6 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, } std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { - // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should - // be signalled at least once, and signaled after a new controller is connected? const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; return styleset_event.readable; } @@ -527,43 +513,43 @@ Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { return last_processed_vibration; } -void Controller_NPad::AddNewController(NPadControllerType controller) { - controller = DecideBestController(controller); - if (controller == NPadControllerType::Handheld) { - connected_controllers[8] = {controller, true}; - InitNewlyAddedControler(8); - return; - } - const auto pos = - std::find_if(connected_controllers.begin(), connected_controllers.end() - 2, - [](const ControllerHolder& holder) { return !holder.is_connected; }); - if (pos == connected_controllers.end() - 2) { - LOG_ERROR(Service_HID, "Cannot connect any more controllers!"); +void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) { + UpdateControllerAt(controller, npad_index, true); +} + +void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, + bool connected) { + if (!connected) { + DisconnectNPad(IndexToNPad(npad_index)); return; } - const auto controller_id = std::distance(connected_controllers.begin(), pos); - connected_controllers[controller_id] = {controller, true}; - InitNewlyAddedControler(controller_id); -} -void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) { - controller = DecideBestController(controller); if (controller == NPadControllerType::Handheld) { - connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true}; - InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD)); + Settings::values.players[HANDHELD_INDEX].controller_type = + MapNPadToSettingsType(controller); + Settings::values.players[HANDHELD_INDEX].connected = true; + connected_controllers[HANDHELD_INDEX] = {controller, true}; + InitNewlyAddedController(HANDHELD_INDEX); return; } - connected_controllers[NPadIdToIndex(npad_id)] = {controller, true}; - InitNewlyAddedControler(NPadIdToIndex(npad_id)); -} - -void Controller_NPad::ConnectNPad(u32 npad_id) { - connected_controllers[NPadIdToIndex(npad_id)].is_connected = true; + Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller); + Settings::values.players[npad_index].connected = true; + connected_controllers[npad_index] = {controller, true}; + InitNewlyAddedController(npad_index); } void Controller_NPad::DisconnectNPad(u32 npad_id) { - connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; + const auto npad_index = NPadIdToIndex(npad_id); + connected_controllers[npad_index].is_connected = false; + Settings::values.players[npad_index].connected = false; + + auto& controller = shared_memory_entries[npad_index]; + controller.joy_styles.raw = 0; // Zero out + controller.device_type.raw = 0; + controller.properties.raw = 0; + + styleset_changed_events[npad_index].writable->Signal(); } void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { @@ -599,8 +585,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) { std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); - InitNewlyAddedControler(npad_index_1); - InitNewlyAddedControler(npad_index_2); + AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1); + AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2); return true; } @@ -628,7 +614,6 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { case 7: return LedPattern{0, 1, 1, 0}; default: - UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id); return LedPattern{0, 0, 0, 0}; } } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 5d4c58a43..75ce5b731 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -118,10 +118,11 @@ public: std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; Vibration GetLastVibration() const; - void AddNewController(NPadControllerType controller); - void AddNewControllerAt(NPadControllerType controller, u32 npad_id); + // Adds a new controller at an index. + void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index); + // Adds a new controller at an index with connection status. + void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); - void ConnectNPad(u32 npad_id); void DisconnectNPad(u32 npad_id); void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; @@ -141,6 +142,8 @@ public: // Specifically for cheat engine and other features. u32 GetAndResetPressState(); + static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type); + static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type); static std::size_t NPadIdToIndex(u32 npad_id); static u32 IndexToNPad(std::size_t index); @@ -309,7 +312,7 @@ private: bool is_connected; }; - void InitNewlyAddedControler(std::size_t controller_idx); + void InitNewlyAddedController(std::size_t controller_idx); bool IsControllerSupported(NPadControllerType controller) const; NPadControllerType DecideBestController(NPadControllerType priority) const; void RequestPadStateUpdate(u32 npad_id); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 1e95b7580..33416b5dd 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -38,11 +38,9 @@ namespace Service::HID { // Updating period for each HID device. -// TODO(ogniK): Find actual polling rate of hid -constexpr auto pad_update_ns = std::chrono::nanoseconds{1000000000 / 66}; -[[maybe_unused]] constexpr auto accelerometer_update_ns = - std::chrono::nanoseconds{1000000000 / 100}; -[[maybe_unused]] constexpr auto gyroscope_update_ticks = std::chrono::nanoseconds{1000000000 / 100}; +// HID is polled every 15ms, this value was derived from +// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet +constexpr auto pad_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.6Hz) constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; IAppletResource::IAppletResource(Core::System& system) @@ -845,8 +843,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto can_vibrate{rp.Pop<bool>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetVibrationEnabled(can_vibrate); + Settings::values.vibration_enabled = can_vibrate; LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); @@ -859,8 +856,7 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push( - applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled()); + rb.Push(Settings::values.vibration_enabled); } void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { diff --git a/src/core/settings.cpp b/src/core/settings.cpp index d328fb8b7..28d3f9099 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -13,56 +13,6 @@ namespace Settings { -namespace NativeButton { -const std::array<const char*, NumButtons> mapping = {{ - "button_a", - "button_b", - "button_x", - "button_y", - "button_lstick", - "button_rstick", - "button_l", - "button_r", - "button_zl", - "button_zr", - "button_plus", - "button_minus", - "button_dleft", - "button_dup", - "button_dright", - "button_ddown", - "button_lstick_left", - "button_lstick_up", - "button_lstick_right", - "button_lstick_down", - "button_rstick_left", - "button_rstick_up", - "button_rstick_right", - "button_rstick_down", - "button_sl", - "button_sr", - "button_home", - "button_screenshot", -}}; -} - -namespace NativeAnalog { -const std::array<const char*, NumAnalogs> mapping = {{ - "lstick", - "rstick", -}}; -} - -namespace NativeMouseButton { -const std::array<const char*, NumMouseButtons> mapping = {{ - "left", - "right", - "middle", - "forward", - "back", -}}; -} - Values values = {}; bool configuring_global = true; diff --git a/src/core/settings.h b/src/core/settings.h index 3681b5e9d..732c6a894 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -12,340 +12,10 @@ #include <string> #include <vector> #include "common/common_types.h" +#include "input_common/settings.h" namespace Settings { -namespace NativeButton { -enum Values { - A, - B, - X, - Y, - LStick, - RStick, - L, - R, - ZL, - ZR, - Plus, - Minus, - - DLeft, - DUp, - DRight, - DDown, - - LStick_Left, - LStick_Up, - LStick_Right, - LStick_Down, - - RStick_Left, - RStick_Up, - RStick_Right, - RStick_Down, - - SL, - SR, - - Home, - Screenshot, - - NumButtons, -}; - -constexpr int BUTTON_HID_BEGIN = A; -constexpr int BUTTON_NS_BEGIN = Home; - -constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN; -constexpr int BUTTON_NS_END = NumButtons; - -constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; -constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; - -extern const std::array<const char*, NumButtons> mapping; - -} // namespace NativeButton - -namespace NativeAnalog { -enum Values { - LStick, - RStick, - - NumAnalogs, -}; - -constexpr int STICK_HID_BEGIN = LStick; -constexpr int STICK_HID_END = NumAnalogs; -constexpr int NUM_STICKS_HID = NumAnalogs; - -extern const std::array<const char*, NumAnalogs> mapping; -} // namespace NativeAnalog - -namespace NativeMouseButton { -enum Values { - Left, - Right, - Middle, - Forward, - Back, - - NumMouseButtons, -}; - -constexpr int MOUSE_HID_BEGIN = Left; -constexpr int MOUSE_HID_END = NumMouseButtons; -constexpr int NUM_MOUSE_HID = NumMouseButtons; - -extern const std::array<const char*, NumMouseButtons> mapping; -} // namespace NativeMouseButton - -namespace NativeKeyboard { -enum Keys { - None, - Error, - - A = 4, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - N1, - N2, - N3, - N4, - N5, - N6, - N7, - N8, - N9, - N0, - Enter, - Escape, - Backspace, - Tab, - Space, - Minus, - Equal, - LeftBrace, - RightBrace, - Backslash, - Tilde, - Semicolon, - Apostrophe, - Grave, - Comma, - Dot, - Slash, - CapsLockKey, - - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - - SystemRequest, - ScrollLockKey, - Pause, - Insert, - Home, - PageUp, - Delete, - End, - PageDown, - Right, - Left, - Down, - Up, - - NumLockKey, - KPSlash, - KPAsterisk, - KPMinus, - KPPlus, - KPEnter, - KP1, - KP2, - KP3, - KP4, - KP5, - KP6, - KP7, - KP8, - KP9, - KP0, - KPDot, - - Key102, - Compose, - Power, - KPEqual, - - F13, - F14, - F15, - F16, - F17, - F18, - F19, - F20, - F21, - F22, - F23, - F24, - - Open, - Help, - Properties, - Front, - Stop, - Repeat, - Undo, - Cut, - Copy, - Paste, - Find, - Mute, - VolumeUp, - VolumeDown, - CapsLockActive, - NumLockActive, - ScrollLockActive, - KPComma, - - KPLeftParenthesis, - KPRightParenthesis, - - LeftControlKey = 0xE0, - LeftShiftKey, - LeftAltKey, - LeftMetaKey, - RightControlKey, - RightShiftKey, - RightAltKey, - RightMetaKey, - - MediaPlayPause, - MediaStopCD, - MediaPrevious, - MediaNext, - MediaEject, - MediaVolumeUp, - MediaVolumeDown, - MediaMute, - MediaWebsite, - MediaBack, - MediaForward, - MediaStop, - MediaFind, - MediaScrollUp, - MediaScrollDown, - MediaEdit, - MediaSleep, - MediaCoffee, - MediaRefresh, - MediaCalculator, - - NumKeyboardKeys, -}; - -static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys."); - -enum Modifiers { - LeftControl, - LeftShift, - LeftAlt, - LeftMeta, - RightControl, - RightShift, - RightAlt, - RightMeta, - CapsLock, - ScrollLock, - NumLock, - - NumKeyboardMods, -}; - -constexpr int KEYBOARD_KEYS_HID_BEGIN = None; -constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys; -constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys; - -constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl; -constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods; -constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods; - -} // namespace NativeKeyboard - -using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; -using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; -using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; -using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; -using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; - -constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; -constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; -constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; -constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E; - -enum class ControllerType { - ProController, - DualJoycon, - RightJoycon, - LeftJoycon, -}; - -struct PlayerInput { - bool connected; - ControllerType type; - ButtonsRaw buttons; - AnalogsRaw analogs; - - u32 body_color_right; - u32 button_color_right; - u32 body_color_left; - u32 button_color_left; -}; - -struct TouchscreenInput { - bool enabled; - std::string device; - - u32 finger; - u32 diameter_x; - u32 diameter_y; - u32 rotation_angle; -}; - enum class RendererBackend { OpenGL = 0, Vulkan = 1, @@ -461,6 +131,8 @@ struct Values { // Controls std::array<PlayerInput, 10> players; + bool use_docked_mode; + bool mouse_enabled; std::string mouse_device; MouseButtonsRaw mouse_buttons; @@ -474,14 +146,15 @@ struct Values { AnalogsRaw debug_pad_analogs; std::string motion_device; + + bool vibration_enabled; + TouchscreenInput touchscreen; std::atomic_bool is_device_reload_pending{true}; std::string udp_input_address; u16 udp_input_port; u8 udp_pad_index; - bool use_docked_mode; - // Data Storage bool use_virtual_sd; bool gamecard_inserted; diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 317c25bad..56267c8a8 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -7,6 +7,8 @@ add_library(input_common STATIC main.h motion_emu.cpp motion_emu.h + settings.cpp + settings.h gcadapter/gc_adapter.cpp gcadapter/gc_adapter.h gcadapter/gc_poller.cpp diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index b346fdf8e..85342bbe7 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -232,7 +232,7 @@ std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::Param const int port = params.Get("port", 0); const int axis_x = params.Get("axis_x", 0); const int axis_y = params.Get("axis_y", 1); - const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); + const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range); diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index b9d5d0ec3..b8725e9af 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -44,7 +44,6 @@ void Init() { #ifdef HAVE_SDL2 sdl = SDL::Init(); #endif - udp = CemuhookUDP::Init(); } @@ -103,6 +102,55 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, return circle_pad_param.Serialize(); } +std::vector<Common::ParamPackage> GetInputDevices() { + std::vector<Common::ParamPackage> devices = { + Common::ParamPackage{{"display", "Any"}, {"class", "any"}}, + Common::ParamPackage{{"display", "Keyboard"}, {"class", "key"}}, + }; +#ifdef HAVE_SDL2 + auto sdl_devices = sdl->GetInputDevices(); + devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); +#endif + auto udp_devices = udp->GetInputDevices(); + devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); + return devices; +} + +std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage> GetButtonMappingForDevice( + const Common::ParamPackage& params) { + std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage> mappings{}; + if (!params.Has("class") || params.Get("class", "") == "any") { + return mappings; + } + if (params.Get("class", "") == "key") { + // TODO consider returning the SDL key codes for the default keybindings + } +#ifdef HAVE_SDL2 + if (params.Get("class", "") == "sdl") { + return sdl->GetButtonMappingForDevice(params); + } +#endif + return mappings; +} + +std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage> GetAnalogMappingForDevice( + const Common::ParamPackage& params) { + std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage> mappings{}; + if (!params.Has("class") || params.Get("class", "") == "any") { + return mappings; + } + if (params.Get("class", "") == "key") { + // TODO consider returning the SDL key codes for the default keybindings + return mappings; + } +#ifdef HAVE_SDL2 + if (params.Get("class", "") == "sdl") { + return sdl->GetAnalogMappingForDevice(params); + } +#endif + return mappings; +} + namespace Polling { std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) { diff --git a/src/input_common/main.h b/src/input_common/main.h index 0e32856f6..ebc7f9533 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -6,8 +6,10 @@ #include <memory> #include <string> +#include <unordered_map> #include <vector> #include "input_common/gcadapter/gc_poller.h" +#include "input_common/settings.h" namespace Common { class ParamPackage; @@ -42,9 +44,27 @@ std::string GenerateKeyboardParam(int key_code); std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, int key_modifier, float modifier_scale); +/** + * Return a list of available input devices that this Factory can create a new device with. + * Each returned Parampackage should have a `display` field used for display, a class field for + * backends to determine if this backend is meant to service the request and any other information + * needed to identify this in the backend later. + */ +std::vector<Common::ParamPackage> GetInputDevices(); + +/** + * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default + * mapping for the device. This is currently only implemented for the sdl backend devices. + */ +using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>; +using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>; + +ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage&); +AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&); + namespace Polling { -enum class DeviceType { Button, Analog }; +enum class DeviceType { Button, AnalogPreferred }; /** * A class that can be used to get inputs from an input device like controllers without having to @@ -54,7 +74,9 @@ class DevicePoller { public: virtual ~DevicePoller() = default; /// Setup and start polling for inputs, should be called before GetNextInput - virtual void Start() = 0; + /// If a device_id is provided, events should be filtered to only include events from this + /// device id + virtual void Start(std::string device_id = "") = 0; /// Stop polling virtual void Stop() = 0; /** diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h index 5306daa70..f3554be9a 100644 --- a/src/input_common/sdl/sdl.h +++ b/src/input_common/sdl/sdl.h @@ -6,6 +6,7 @@ #include <memory> #include <vector> +#include "common/param_package.h" #include "input_common/main.h" namespace InputCommon::Polling { @@ -22,14 +23,24 @@ public: /// Unregisters SDL device factories and shut them down. virtual ~State() = default; - virtual Pollers GetPollers(Polling::DeviceType type) = 0; + virtual Pollers GetPollers(Polling::DeviceType type) { + return {}; + } + + virtual std::vector<Common::ParamPackage> GetInputDevices() { + return {}; + } + + virtual ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage&) { + return {}; + } + virtual AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&) { + return {}; + } }; class NullState : public State { public: - Pollers GetPollers(Polling::DeviceType type) override { - return {}; - } }; std::unique_ptr<State> Init(); diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index d76c279d3..35a9d45ec 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -7,6 +7,8 @@ #include <cmath> #include <functional> #include <mutex> +#include <optional> +#include <sstream> #include <string> #include <thread> #include <tuple> @@ -23,7 +25,8 @@ namespace InputCommon::SDL { -static std::string GetGUID(SDL_Joystick* joystick) { +namespace { +std::string GetGUID(SDL_Joystick* joystick) { const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); char guid_str[33]; SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); @@ -31,7 +34,8 @@ static std::string GetGUID(SDL_Joystick* joystick) { } /// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice -static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event); +Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event); +} // Anonymous namespace static int SDLEventWatcher(void* user_data, SDL_Event* event) { auto* const sdl_state = static_cast<SDLState*>(user_data); @@ -48,8 +52,10 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) { class SDLJoystick { public: - SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick) - : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {} + SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, + SDL_GameController* gamecontroller) + : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, + sdl_controller{gamecontroller, &SDL_GameControllerClose} {} void SetButton(int button, bool value) { std::lock_guard lock{mutex}; @@ -115,10 +121,15 @@ public: return sdl_joystick.get(); } - void SetSDLJoystick(SDL_Joystick* joystick) { + void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) { + sdl_controller.reset(controller); sdl_joystick.reset(joystick); } + SDL_GameController* GetSDLGameController() const { + return sdl_controller.get(); + } + private: struct State { std::unordered_map<int, bool> buttons; @@ -128,6 +139,7 @@ private: std::string guid; int port; std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; + std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; mutable std::mutex mutex; }; @@ -136,18 +148,19 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g const auto it = joystick_map.find(guid); if (it != joystick_map.end()) { while (it->second.size() <= static_cast<std::size_t>(port)) { - auto joystick = - std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr); + auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), + nullptr, nullptr); it->second.emplace_back(std::move(joystick)); } return it->second[port]; } - auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr); + auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr); return joystick_map[guid].emplace_back(std::move(joystick)); } std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); + auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id); const std::string guid = GetGUID(sdl_joystick); std::lock_guard lock{joystick_map_mutex}; @@ -171,23 +184,27 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_ }); if (nullptr_it != map_it->second.end()) { // ... and map it - (*nullptr_it)->SetSDLJoystick(sdl_joystick); + (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller); return *nullptr_it; } // There is no SDLJoystick without a mapped SDL_Joystick // Create a new SDLJoystick const int port = static_cast<int>(map_it->second.size()); - auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); + auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller); return map_it->second.emplace_back(std::move(joystick)); } - auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); + auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller); return joystick_map[guid].emplace_back(std::move(joystick)); } void SDLState::InitJoystick(int joystick_index) { SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); + SDL_GameController* sdl_gamecontroller = nullptr; + if (SDL_IsGameController(joystick_index)) { + sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); + } if (!sdl_joystick) { LOG_ERROR(Input, "failed to open joystick {}", joystick_index); return; @@ -196,7 +213,7 @@ void SDLState::InitJoystick(int joystick_index) { std::lock_guard lock{joystick_map_mutex}; if (joystick_map.find(guid) == joystick_map.end()) { - auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); + auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); joystick_map[guid].emplace_back(std::move(joystick)); return; } @@ -205,11 +222,11 @@ void SDLState::InitJoystick(int joystick_index) { joystick_guid_list.begin(), joystick_guid_list.end(), [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); if (it != joystick_guid_list.end()) { - (*it)->SetSDLJoystick(sdl_joystick); + (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); return; } const int port = static_cast<int>(joystick_guid_list.size()); - auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); + auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); joystick_guid_list.emplace_back(std::move(joystick)); } @@ -231,7 +248,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { // Destruct SDL_Joystick outside the lock guard because SDL can internally call the // event callback which locks the mutex again. - joystick->SetSDLJoystick(nullptr); + joystick->SetSDLJoystick(nullptr, nullptr); } void SDLState::HandleGameControllerEvent(const SDL_Event& event) { @@ -460,7 +477,7 @@ public: const int port = params.Get("port", 0); const int axis_x = params.Get("axis_x", 0); const int axis_y = params.Get("axis_y", 1); - const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); + const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); auto joystick = state.GetSDLJoystickByGUID(guid, port); @@ -476,8 +493,10 @@ private: SDLState::SDLState() { using namespace Input; - RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this)); - RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this)); + analog_factory = std::make_shared<SDLAnalogFactory>(*this); + button_factory = std::make_shared<SDLButtonFactory>(*this); + RegisterFactory<AnalogDevice>("sdl", analog_factory); + RegisterFactory<ButtonDevice>("sdl", button_factory); // If the frontend is going to manage the event loop, then we dont start one here start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); @@ -485,6 +504,7 @@ SDLState::SDLState() { LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); return; } + has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError()); } @@ -497,7 +517,7 @@ SDLState::SDLState() { using namespace std::chrono_literals; while (initialized) { SDL_PumpEvents(); - std::this_thread::sleep_for(10ms); + std::this_thread::sleep_for(5ms); } }); } @@ -523,65 +543,233 @@ SDLState::~SDLState() { } } -static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { +std::vector<Common::ParamPackage> SDLState::GetInputDevices() { + std::scoped_lock lock(joystick_map_mutex); + std::vector<Common::ParamPackage> devices = {}; + for (const auto& [key, value] : joystick_map) { + for (const auto& joystick : value) { + auto controller = joystick->GetSDLGameController(); + auto joy = joystick->GetSDLJoystick(); + if (controller) { + std::string name = + fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort()); + devices.emplace_back(Common::ParamPackage{ + {"class", "sdl"}, + {"display", name}, + {"guid", joystick->GetGUID()}, + {"port", std::to_string(joystick->GetPort())}, + }); + } else if (joy) { + std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort()); + devices.emplace_back(Common::ParamPackage{ + {"class", "sdl"}, + {"display", name}, + {"guid", joystick->GetGUID()}, + {"port", std::to_string(joystick->GetPort())}, + }); + } + } + } + return devices; +} + +namespace { +Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis, + float value = 0.1) { + Common::ParamPackage params({{"engine", "sdl"}}); + params.Set("port", port); + params.Set("guid", guid); + params.Set("axis", axis); + if (value > 0) { + params.Set("direction", "+"); + params.Set("threshold", "0.5"); + } else { + params.Set("direction", "-"); + params.Set("threshold", "-0.5"); + } + return params; +} + +Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) { + Common::ParamPackage params({{"engine", "sdl"}}); + params.Set("port", port); + params.Set("guid", guid); + params.Set("button", button); + return params; +} + +Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) { Common::ParamPackage params({{"engine", "sdl"}}); + params.Set("port", port); + params.Set("guid", guid); + params.Set("hat", hat); + switch (value) { + case SDL_HAT_UP: + params.Set("direction", "up"); + break; + case SDL_HAT_DOWN: + params.Set("direction", "down"); + break; + case SDL_HAT_LEFT: + params.Set("direction", "left"); + break; + case SDL_HAT_RIGHT: + params.Set("direction", "right"); + break; + default: + return {}; + } + return params; +} + +Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { + Common::ParamPackage params{}; + switch (event.type) { case SDL_JOYAXISMOTION: { const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); - params.Set("port", joystick->GetPort()); - params.Set("guid", joystick->GetGUID()); - params.Set("axis", event.jaxis.axis); - if (event.jaxis.value > 0) { - params.Set("direction", "+"); - params.Set("threshold", "0.5"); - } else { - params.Set("direction", "-"); - params.Set("threshold", "-0.5"); - } + params = BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jaxis.axis, event.jaxis.value); break; } case SDL_JOYBUTTONUP: { const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); - params.Set("port", joystick->GetPort()); - params.Set("guid", joystick->GetGUID()); - params.Set("button", event.jbutton.button); + params = BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jbutton.button); break; } case SDL_JOYHATMOTION: { const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); - params.Set("port", joystick->GetPort()); - params.Set("guid", joystick->GetGUID()); - params.Set("hat", event.jhat.hat); - switch (event.jhat.value) { - case SDL_HAT_UP: - params.Set("direction", "up"); - break; - case SDL_HAT_DOWN: - params.Set("direction", "down"); - break; - case SDL_HAT_LEFT: - params.Set("direction", "left"); - break; - case SDL_HAT_RIGHT: - params.Set("direction", "right"); - break; - default: - return {}; - } + params = BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), + event.jhat.hat, event.jhat.value); break; } } return params; } -namespace Polling { +Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, + const SDL_GameControllerButtonBind& binding) { + Common::ParamPackage out{}; + switch (binding.bindType) { + case SDL_CONTROLLER_BINDTYPE_AXIS: + out = BuildAnalogParamPackageForButton(port, guid, binding.value.axis); + break; + case SDL_CONTROLLER_BINDTYPE_BUTTON: + out = BuildButtonParamPackageForButton(port, guid, binding.value.button); + break; + case SDL_CONTROLLER_BINDTYPE_HAT: + out = BuildHatParamPackageForButton(port, guid, binding.value.hat.hat, + binding.value.hat.hat_mask); + break; + default: + break; + } + return out; +}; + +Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x, + int axis_y) { + Common::ParamPackage params{}; + params.Set("engine", "sdl"); + params.Set("port", port); + params.Set("guid", guid); + params.Set("axis_x", axis_x); + params.Set("axis_y", axis_y); + return params; +} +} // Anonymous namespace + +ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) { + // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. + // We will add those afterwards + // This list also excludes Screenshot since theres not really a mapping for that + std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerButton> + switch_to_sdl_button = { + {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B}, + {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A}, + {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y}, + {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X}, + {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, + {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, + {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, + {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, + {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START}, + {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK}, + {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, + {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, + {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, + {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, + {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, + {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, + {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, + }; + if (!params.Has("guid") || !params.Has("port")) { + return {}; + } + const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); + auto controller = joystick->GetSDLGameController(); + if (!controller) { + return {}; + } + + ButtonMapping mapping{}; + for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { + const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); + mapping[switch_button] = + BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding); + } + + // Add the missing bindings for ZL/ZR + std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerAxis> switch_to_sdl_axis = + { + {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, + {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, + }; + for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { + const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); + mapping[switch_button] = + BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding); + } + + return mapping; +} + +AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) { + if (!params.Has("guid") || !params.Has("port")) { + return {}; + } + const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); + auto controller = joystick->GetSDLGameController(); + if (!controller) { + return {}; + } + + AnalogMapping mapping = {}; + const auto& binding_left_x = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); + const auto& binding_left_y = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); + mapping[Settings::NativeAnalog::LStick] = + BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), + binding_left_x.value.axis, binding_left_y.value.axis); + const auto& binding_right_x = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); + const auto& binding_right_y = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); + mapping[Settings::NativeAnalog::RStick] = + BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), + binding_right_x.value.axis, binding_right_y.value.axis); + return mapping; +} +namespace Polling { class SDLPoller : public InputCommon::Polling::DevicePoller { public: explicit SDLPoller(SDLState& state_) : state(state_) {} - void Start() override { + void Start(std::string device_id) override { state.event_queue.Clear(); state.polling = true; } @@ -601,71 +789,106 @@ public: Common::ParamPackage GetNextInput() override { SDL_Event event; while (state.event_queue.Pop(event)) { - switch (event.type) { - case SDL_JOYAXISMOTION: - if (std::abs(event.jaxis.value / 32767.0) < 0.5) { - break; - } - [[fallthrough]]; - case SDL_JOYBUTTONUP: - case SDL_JOYHATMOTION: - return SDLEventToButtonParamPackage(state, event); + const auto package = FromEvent(event); + if (package) { + return *package; } } return {}; } + std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) { + switch (event.type) { + case SDL_JOYAXISMOTION: + if (std::abs(event.jaxis.value / 32767.0) < 0.5) { + break; + } + [[fallthrough]]; + case SDL_JOYBUTTONUP: + case SDL_JOYHATMOTION: + return {SDLEventToButtonParamPackage(state, event)}; + } + return {}; + } }; -class SDLAnalogPoller final : public SDLPoller { +/** + * Attempts to match the press to a controller joy axis (left/right stick) and if a match + * isn't found, checks if the event matches anything from SDLButtonPoller and uses that + * instead + */ +class SDLAnalogPreferredPoller final : public SDLPoller { public: - explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {} - - void Start() override { - SDLPoller::Start(); + explicit SDLAnalogPreferredPoller(SDLState& state_) + : SDLPoller(state_), button_poller(state_) {} + void Start(std::string device_id) override { + SDLPoller::Start(device_id); + // Load the game controller // Reset stored axes analog_x_axis = -1; analog_y_axis = -1; - analog_axes_joystick = -1; } Common::ParamPackage GetNextInput() override { SDL_Event event; while (state.event_queue.Pop(event)) { - if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { + // Filter out axis events that are below a threshold + if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) { continue; } - // An analog device needs two axes, so we need to store the axis for later and wait for - // a second SDL event. The axes also must be from the same joystick. - const int axis = event.jaxis.axis; - if (analog_x_axis == -1) { - analog_x_axis = axis; - analog_axes_joystick = event.jaxis.which; - } else if (analog_y_axis == -1 && analog_x_axis != axis && - analog_axes_joystick == event.jaxis.which) { - analog_y_axis = axis; + // Simplify controller config by testing if game controller support is enabled. + if (event.type == SDL_JOYAXISMOTION) { + const auto axis = event.jaxis.axis; + const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); + const auto controller = joystick->GetSDLGameController(); + if (controller) { + const auto axis_left_x = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) + .value.axis; + const auto axis_left_y = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) + .value.axis; + const auto axis_right_x = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) + .value.axis; + const auto axis_right_y = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) + .value.axis; + + if (axis == axis_left_x || axis == axis_left_y) { + analog_x_axis = axis_left_x; + analog_y_axis = axis_left_y; + break; + } else if (axis == axis_right_x || axis == axis_right_y) { + analog_x_axis = axis_right_x; + analog_y_axis = axis_right_y; + break; + } + } + } + + // If the press wasn't accepted as a joy axis, check for a button press + auto button_press = button_poller.FromEvent(event); + if (button_press) { + return *button_press; } } - Common::ParamPackage params; + if (analog_x_axis != -1 && analog_y_axis != -1) { const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); - params.Set("engine", "sdl"); - params.Set("port", joystick->GetPort()); - params.Set("guid", joystick->GetGUID()); - params.Set("axis_x", analog_x_axis); - params.Set("axis_y", analog_y_axis); + auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), + analog_x_axis, analog_y_axis); analog_x_axis = -1; analog_y_axis = -1; - analog_axes_joystick = -1; return params; } - return params; + return {}; } private: int analog_x_axis = -1; int analog_y_axis = -1; - SDL_JoystickID analog_axes_joystick = -1; + SDLButtonPoller button_poller; }; } // namespace Polling @@ -673,8 +896,8 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) { Pollers pollers; switch (type) { - case InputCommon::Polling::DeviceType::Analog: - pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); + case InputCommon::Polling::DeviceType::AnalogPreferred: + pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this)); break; case InputCommon::Polling::DeviceType::Button: pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index 606a32c5b..bd19ba61d 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -50,6 +50,11 @@ public: std::atomic<bool> polling = false; Common::SPSCQueue<SDL_Event> event_queue; + std::vector<Common::ParamPackage> GetInputDevices() override; + + ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; + AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; + private: void InitJoystick(int joystick_index); void CloseJoystick(SDL_Joystick* sdl_joystick); @@ -57,6 +62,9 @@ private: /// Needs to be called before SDL_QuitSubSystem. void CloseJoysticks(); + // Set to true if SDL supports game controller subsystem + bool has_gamecontroller = false; + /// Map of GUID of a list of corresponding virtual Joysticks std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; std::mutex joystick_map_mutex; diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp new file mode 100644 index 000000000..80c719cf4 --- /dev/null +++ b/src/input_common/settings.cpp @@ -0,0 +1,33 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "input_common/settings.h" + +namespace Settings { +namespace NativeButton { +const std::array<const char*, NumButtons> mapping = {{ + "button_a", "button_b", "button_x", "button_y", "button_lstick", + "button_rstick", "button_l", "button_r", "button_zl", "button_zr", + "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright", + "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot", +}}; +} + +namespace NativeAnalog { +const std::array<const char*, NumAnalogs> mapping = {{ + "lstick", + "rstick", +}}; +} + +namespace NativeMouseButton { +const std::array<const char*, NumMouseButtons> mapping = {{ + "left", + "right", + "middle", + "forward", + "back", +}}; +} +} // namespace Settings diff --git a/src/input_common/settings.h b/src/input_common/settings.h new file mode 100644 index 000000000..8e481a7fe --- /dev/null +++ b/src/input_common/settings.h @@ -0,0 +1,335 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <string> +#include "common/common_types.h" + +namespace Settings { +namespace NativeButton { +enum Values { + A, + B, + X, + Y, + LStick, + RStick, + L, + R, + ZL, + ZR, + Plus, + Minus, + + DLeft, + DUp, + DRight, + DDown, + + SL, + SR, + + Home, + Screenshot, + + NumButtons, +}; + +constexpr int BUTTON_HID_BEGIN = A; +constexpr int BUTTON_NS_BEGIN = Home; + +constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN; +constexpr int BUTTON_NS_END = NumButtons; + +constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; +constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; + +extern const std::array<const char*, NumButtons> mapping; + +} // namespace NativeButton + +namespace NativeAnalog { +enum Values { + LStick, + RStick, + + NumAnalogs, +}; + +constexpr int STICK_HID_BEGIN = LStick; +constexpr int STICK_HID_END = NumAnalogs; +constexpr int NUM_STICKS_HID = NumAnalogs; + +extern const std::array<const char*, NumAnalogs> mapping; +} // namespace NativeAnalog + +namespace NativeMouseButton { +enum Values { + Left, + Right, + Middle, + Forward, + Back, + + NumMouseButtons, +}; + +constexpr int MOUSE_HID_BEGIN = Left; +constexpr int MOUSE_HID_END = NumMouseButtons; +constexpr int NUM_MOUSE_HID = NumMouseButtons; + +extern const std::array<const char*, NumMouseButtons> mapping; +} // namespace NativeMouseButton + +namespace NativeKeyboard { +enum Keys { + None, + Error, + + A = 4, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + N1, + N2, + N3, + N4, + N5, + N6, + N7, + N8, + N9, + N0, + Enter, + Escape, + Backspace, + Tab, + Space, + Minus, + Equal, + LeftBrace, + RightBrace, + Backslash, + Tilde, + Semicolon, + Apostrophe, + Grave, + Comma, + Dot, + Slash, + CapsLockKey, + + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + + SystemRequest, + ScrollLockKey, + Pause, + Insert, + Home, + PageUp, + Delete, + End, + PageDown, + Right, + Left, + Down, + Up, + + NumLockKey, + KPSlash, + KPAsterisk, + KPMinus, + KPPlus, + KPEnter, + KP1, + KP2, + KP3, + KP4, + KP5, + KP6, + KP7, + KP8, + KP9, + KP0, + KPDot, + + Key102, + Compose, + Power, + KPEqual, + + F13, + F14, + F15, + F16, + F17, + F18, + F19, + F20, + F21, + F22, + F23, + F24, + + Open, + Help, + Properties, + Front, + Stop, + Repeat, + Undo, + Cut, + Copy, + Paste, + Find, + Mute, + VolumeUp, + VolumeDown, + CapsLockActive, + NumLockActive, + ScrollLockActive, + KPComma, + + KPLeftParenthesis, + KPRightParenthesis, + + LeftControlKey = 0xE0, + LeftShiftKey, + LeftAltKey, + LeftMetaKey, + RightControlKey, + RightShiftKey, + RightAltKey, + RightMetaKey, + + MediaPlayPause, + MediaStopCD, + MediaPrevious, + MediaNext, + MediaEject, + MediaVolumeUp, + MediaVolumeDown, + MediaMute, + MediaWebsite, + MediaBack, + MediaForward, + MediaStop, + MediaFind, + MediaScrollUp, + MediaScrollDown, + MediaEdit, + MediaSleep, + MediaCoffee, + MediaRefresh, + MediaCalculator, + + NumKeyboardKeys, +}; + +static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys."); + +enum Modifiers { + LeftControl, + LeftShift, + LeftAlt, + LeftMeta, + RightControl, + RightShift, + RightAlt, + RightMeta, + CapsLock, + ScrollLock, + NumLock, + + NumKeyboardMods, +}; + +constexpr int KEYBOARD_KEYS_HID_BEGIN = None; +constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys; +constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys; + +constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl; +constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods; +constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods; + +} // namespace NativeKeyboard + +using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; +using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; +using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; +using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; +using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; + +constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; +constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; +constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; +constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E; + +enum class ControllerType { + ProController, + DualJoyconDetached, + LeftJoycon, + RightJoycon, + Handheld, +}; + +struct PlayerInput { + bool connected; + ControllerType controller_type; + ButtonsRaw buttons; + AnalogsRaw analogs; + std::string lstick_mod; + std::string rstick_mod; + + u32 body_color_left; + u32 body_color_right; + u32 button_color_left; + u32 button_color_right; +}; + +struct TouchscreenInput { + bool enabled; + std::string device; + + u32 finger; + u32 diameter_x; + u32 diameter_y; + u32 rotation_angle; +}; +} // namespace Settings diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index 8c6ef1394..60cf47123 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -77,10 +77,11 @@ State::State() { std::make_unique<Client>(status, Settings::values.udp_input_address, Settings::values.udp_input_port, Settings::values.udp_pad_index); - Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", - std::make_shared<UDPTouchFactory>(status)); - Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", - std::make_shared<UDPMotionFactory>(status)); + motion_factory = std::make_shared<UDPMotionFactory>(status); + touch_factory = std::make_shared<UDPTouchFactory>(status); + + Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory); + Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory); } State::~State() { @@ -88,6 +89,12 @@ State::~State() { Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); } +std::vector<Common::ParamPackage> State::GetInputDevices() { + std::vector<Common::ParamPackage> devices = {}; + // TODO support binding udp devices + return devices; +} + void State::ReloadUDPClient() { client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, Settings::values.udp_pad_index); diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h index 4f83f0441..24f6e0857 100644 --- a/src/input_common/udp/udp.h +++ b/src/input_common/udp/udp.h @@ -5,19 +5,26 @@ #pragma once #include <memory> +#include <vector> +#include "common/param_package.h" namespace InputCommon::CemuhookUDP { class Client; +class UDPMotionFactory; +class UDPTouchFactory; class State { public: State(); ~State(); void ReloadUDPClient(); + std::vector<Common::ParamPackage> GetInputDevices(); private: std::unique_ptr<Client> client; + std::shared_ptr<UDPMotionFactory> motion_factory; + std::shared_ptr<UDPTouchFactory> touch_factory; }; std::unique_ptr<State> Init(); diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 656096c9f..6987e85e1 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -39,6 +39,9 @@ add_executable(yuzu configuration/configure_debug.cpp configuration/configure_debug.h configuration/configure_debug.ui + configuration/configure_debug_controller.cpp + configuration/configure_debug_controller.h + configuration/configure_debug_controller.ui configuration/configure_dialog.cpp configuration/configure_dialog.h configuration/configure_filesystem.cpp @@ -62,9 +65,9 @@ add_executable(yuzu configuration/configure_input_player.cpp configuration/configure_input_player.h configuration/configure_input_player.ui - configuration/configure_input_simple.cpp - configuration/configure_input_simple.h - configuration/configure_input_simple.ui + configuration/configure_input_advanced.cpp + configuration/configure_input_advanced.h + configuration/configure_input_advanced.ui configuration/configure_mouse_advanced.cpp configuration/configure_mouse_advanced.h configuration/configure_mouse_advanced.ui diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 7af974d8d..489877be9 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -6,7 +6,6 @@ #include <QKeySequence> #include <QSettings> #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" @@ -32,29 +31,29 @@ 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<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 int Config::default_lstick_mod = Qt::Key_E; +const int Config::default_rstick_mod = 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, @@ -243,10 +242,10 @@ void Config::ReadPlayerValues() { player.connected = ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).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)) + static_cast<u8>(Settings::ControllerType::ProController)) .toUInt()); player.body_color_left = qt_config @@ -300,12 +299,6 @@ void Config::ReadPlayerValues() { } } } - - 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 Config::ReadDebugValues() { @@ -397,13 +390,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")); @@ -433,6 +419,8 @@ void Config::ReadControlValues() { ReadMouseValues(); ReadTouchscreenValues(); + Settings::values.vibration_enabled = + ReadSetting(QStringLiteral("vibration_enabled"), true).toBool(); Settings::values.motion_device = ReadSetting(QStringLiteral("motion_device"), QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")) @@ -501,7 +489,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 +503,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 = @@ -548,8 +536,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); @@ -788,14 +775,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(); } @@ -869,8 +853,9 @@ void Config::SavePlayerValues() { const auto& player = Settings::values.players[p]; 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)); + WriteSetting(QStringLiteral("player_%1_type").arg(p), + static_cast<u8>(player.controller_type), + static_cast<u8>(Settings::ControllerType::ProController)); WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left, Settings::JOYCON_BODY_NEON_BLUE); @@ -990,6 +975,7 @@ void Config::SaveControlValues() { SaveMouseValues(); SaveTouchscreenValues(); + WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true); WriteSetting(QStringLiteral("motion_device"), QString::fromStdString(Settings::values.motion_device), QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); @@ -1036,7 +1022,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(); } @@ -1049,7 +1035,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); @@ -1076,8 +1062,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; @@ -1266,7 +1251,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); diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index e5f39b040..9eeaf9d1e 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -23,7 +23,9 @@ public: void Save(); 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<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; + static const int default_lstick_mod; + static const int default_rstick_mod; static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons; static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; @@ -37,7 +39,6 @@ private: void ReadKeyboardValues(); void ReadMouseValues(); void ReadTouchscreenValues(); - void ApplyDefaultProfileIfInputInvalid(); // Read functions bases off the respective config section names. void ReadAudioValues(); diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index 5f5d8e571..fcf42cdcb 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> diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp new file mode 100644 index 000000000..45996b73f --- /dev/null +++ b/src/yuzu/configuration/configure_debug_controller.cpp @@ -0,0 +1,39 @@ +// 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" + +ConfigureDebugController::ConfigureDebugController(QWidget* parent) + : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()) { + ui->setupUi(this); + + debug_controller = new ConfigureInputPlayer(this, 9, nullptr, true); + 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..df359a4f3 --- /dev/null +++ b/src/yuzu/configuration/configure_debug_controller.h @@ -0,0 +1,33 @@ +// 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> +#include "yuzu/configuration/configure_input_player.h" + +class QPushButton; + +namespace Ui { +class ConfigureDebugController; +} + +class ConfigureDebugController : public QDialog { + Q_OBJECT + +public: + explicit ConfigureDebugController(QWidget* parent); + ~ConfigureDebugController() override; + + void ApplyConfiguration(); + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + ConfigureInputPlayer* debug_controller; + + std::unique_ptr<Ui::ConfigureDebugController> ui; +}; diff --git a/src/yuzu/configuration/configure_debug_controller.ui b/src/yuzu/configuration/configure_debug_controller.ui new file mode 100644 index 000000000..a95ed50ff --- /dev/null +++ b/src/yuzu/configuration/configure_debug_controller.ui @@ -0,0 +1,97 @@ +<?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> + <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>ConfigureDebugController</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> + </connections> +</ui> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 4e30dc51e..857577591 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -80,12 +80,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 +117,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 +138,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_input.cpp b/src/yuzu/configuration/configure_input.cpp index f2977719c..5200d2d0e 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -8,18 +8,32 @@ #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_mouse_advanced.h" +#include "yuzu/configuration/configure_touchscreen_advanced.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 +62,93 @@ 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>()) { ui->setupUi(this); - 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, + player_controllers = { + new ConfigureInputPlayer(this, 0, ui->consoleInputSettings), + new ConfigureInputPlayer(this, 1, ui->consoleInputSettings), + new ConfigureInputPlayer(this, 2, ui->consoleInputSettings), + new ConfigureInputPlayer(this, 3, ui->consoleInputSettings), + new ConfigureInputPlayer(this, 4, ui->consoleInputSettings), + new ConfigureInputPlayer(this, 5, ui->consoleInputSettings), + new ConfigureInputPlayer(this, 6, ui->consoleInputSettings), + new ConfigureInputPlayer(this, 7, ui->consoleInputSettings), }; - 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(); - - connect(ui->restore_defaults_button, &QPushButton::clicked, this, - &ConfigureInput::RestoreDefaults); + player_connected = { + ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected, + ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected, + ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, + }; - for (auto* enabled : players_controller) { - connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this, - &ConfigureInput::UpdateUIEnabled); + 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, + [&] { UpdateAllInputDevices(); }); + connect(player_connected[i], &QCheckBox::stateChanged, + [&, i](int state) { player_controllers[i]->ConnectPlayer(state == Qt::Checked); }); } - 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); }); - } - - connect(ui->handheld_configure, &QPushButton::clicked, this, - [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 8, false); }); - - connect(ui->debug_configure, &QPushButton::clicked, this, - [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 9, true); }); - - connect(ui->mouse_advanced, &QPushButton::clicked, this, + // Only the first player can choose handheld mode so connect the signal just to player 1 + connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged, + [&](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] { CallConfigureDialog<ConfigureDebugController>(*this); }); + connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this] { CallConfigureDialog<ConfigureMouseAdvanced>(*this); }); - - connect(ui->touchscreen_advanced, &QPushButton::clicked, this, + connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog, [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); + + connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); + connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); }); + + RetranslateUI(); + LoadConfiguration(); } ConfigureInput::~ConfigureInput() = default; -void ConfigureInput::ApplyConfiguration() { - for (std::size_t i = 0; i < players_controller.size(); ++i) { - const auto controller_type_index = players_controller[i]->currentIndex(); - - Settings::values.players[i].connected = controller_type_index != 0; +QList<QWidget*> ConfigureInput::GetSubTabs() const { + return { + ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5, + ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, ui->tabAdvanced, + }; +} - 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; - } +void ConfigureInput::ApplyConfiguration() { + for (auto controller : player_controllers) { + controller->ApplyConfiguration(); } + advanced->ApplyConfiguration(); + const bool pre_docked_mode = Settings::values.use_docked_mode; - Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); + Settings::values.use_docked_mode = ui->radioDocked->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 = ui->vibrationGroup->isChecked(); } void ConfigureInput::changeEvent(QEvent* event) { @@ -146,94 +156,63 @@ 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[0].controller_type == + Settings::ControllerType::Handheld); -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; - } - } + ui->vibrationGroup->setChecked(Settings::values.vibration_enabled); +} - for (std::size_t i = 0; i < players_controller.size(); ++i) { - players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0); +void ConfigureInput::LoadPlayerControllerIndices() { + for (std::size_t i = 0; i < player_connected.size(); ++i) { + const auto connected = Settings::values.players[i].connected || + (i == 0 && Settings::values.players[8].connected); + player_connected[i]->setChecked(connected); } +} - 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::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(); } -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::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); +} - LoadPlayerControllerIndices(); +void ConfigureInput::UpdateDockedState(bool is_handheld) { + // If the controller type is handheld only, disallow changing docked mode + ui->radioDocked->setEnabled(!is_handheld); + ui->radioUndocked->setEnabled(!is_handheld); - 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(); -} + ui->radioDocked->setChecked(Settings::values.use_docked_mode); + ui->radioUndocked->setChecked(!Settings::values.use_docked_mode); -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); + // If its handheld only, force docked mode off (since you can't play handheld in a dock) + if (is_handheld) { + ui->radioUndocked->setChecked(true); } } -void ConfigureInput::RestoreDefaults() { - players_controller[0]->setCurrentIndex(2); - - for (std::size_t i = 1; i < players_controller.size(); ++i) { - players_controller[i]->setCurrentIndex(0); +void ConfigureInput::UpdateAllInputDevices() { + for (const auto& player : player_controllers) { + player->UpdateInputDevices(); } - - 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..8241d23ef 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -10,11 +10,14 @@ #include <QDialog> #include <QKeyEvent> +#include "yuzu/configuration/configure_input_advanced.h" +#include "yuzu/configuration/configure_input_player.h" + #include "ui_configure_input.h" -class QPushButton; class QString; class QTimer; +class QCheckBox; namespace Ui { class ConfigureInput; @@ -22,22 +25,25 @@ 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 + /// 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(); /// Load configuration settings. void LoadConfiguration(); @@ -48,6 +54,8 @@ private: std::unique_ptr<Ui::ConfigureInput> ui; - std::array<QComboBox*, 8> players_controller; - std::array<QPushButton*, 8> players_configure; + 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..136955224 100644 --- a/src/yuzu/configuration/configure_input.ui +++ b/src/yuzu/configuration/configure_input.ui @@ -1,529 +1,554 @@ <?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>700</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>3</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="QSpinBox" name="vibrationSpin"> + <property name="minimumSize"> + <size> + <width>65</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>65</width> + <height>16777215</height> + </size> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>200</number> + </property> + <property name="value"> + <number>100</number> + </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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</string> + </property> + <property name="text"> + <string>Configure</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item alignment="Qt::AlignVCenter"> + <widget class="QWidget" name="widget" 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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</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: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</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: 55px;</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..18db04e7e --- /dev/null +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -0,0 +1,169 @@ +// 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(new 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(static_cast<int>(player_idx), + static_cast<int>(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(); }); + + LoadConfiguration(); +} + +ConfigureInputAdvanced::~ConfigureInputAdvanced() = default; + +void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int 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: 55px;") + .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[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[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: 55px;") + .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..d6e913675 --- /dev/null +++ b/src/yuzu/configuration/configure_input_advanced.h @@ -0,0 +1,44 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#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(); + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + void UpdateUIEnabled(); + + void OnControllerButtonClick(int player_idx, int 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..5958435fc --- /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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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..4d79a51f3 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -5,38 +5,86 @@ #include <algorithm> #include <memory> #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/main.h" #include "ui_configure_input_player.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" +constexpr std::size_t HANDHELD_INDEX = 8; + const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> ConfigureInputPlayer::analog_sub_buttons{{ "up", "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 { + +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::Key_Shift: return QObject::tr("Shift"); @@ -51,9 +99,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 +116,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]"); } @@ -111,7 +166,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 +216,24 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string } return QObject::tr("[unknown]"); } +} // namespace -ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug) - : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), +ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, + QWidget* bottom_row, bool debug) + : QWidget(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>()) { + 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, + ui->buttonLStickMod, ui->buttonRStickMod, }; analog_map_buttons = {{ @@ -185,208 +242,159 @@ 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, - }; - - 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) { 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); }); - connect(button, &QPushButton::customContextMenuRequested, - [=, this](const QPoint& menu_location) { - QMenu context_menu; - context_menu.addAction(tr("Clear"), [&] { - 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 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]); } - 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++) { + // Handle clicks for the modifier buttons as well. + ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_lstick_mod); + ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_rstick_mod); + + 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] { 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); }); - 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]); - 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); - } + + connect(analog_map_modifier_button[analog_id], &QPushButton::clicked, [=, this] { + HandleClick( + analog_map_modifier_button[analog_id], + [=, this](const Common::ParamPackage& params) { + SetAnalogParam(params, analogs_param[analog_id], "modifier"); + }, + InputCommon::Polling::DeviceType::AnalogPreferred); }); - connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, + 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); + }); } - connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); - connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); }); + // Player Connected checkbox + connect(ui->groupConnectedController, &QGroupBox::toggled, + [&](bool checked) { emit Connected(checked); }); + + // Set up controller type. Only Player 1 can choose Handheld. + ui->comboControllerType->clear(); + + QStringList controller_types = { + QStringLiteral("Pro Controller"), + QStringLiteral("Dual Joycons"), + QStringLiteral("Left Joycon"), + QStringLiteral("Right Joycon"), + }; + + if (player_index == 0) { + controller_types.append(QStringLiteral("Handheld")); + connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), + [&](int index) { + emit HandheldStateChanged(GetControllerTypeFromIndex(index) == + Settings::ControllerType::Handheld); + }); + } + + // The Debug Controller can only choose the Pro Controller. + if (debug) { + ui->buttonScreenshot->setEnabled(false); + ui->buttonHome->setEnabled(false); + ui->groupConnectedController->setCheckable(false); + QStringList debug_controller_types = { + QStringLiteral("Pro Controller"), + }; + ui->comboControllerType->addItems(debug_controller_types); + } else { + ui->comboControllerType->addItems(controller_types); + } + + UpdateControllerIcon(); + UpdateControllerAvailableButtons(); + connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [&](int) { + UpdateControllerIcon(); + UpdateControllerAvailableButtons(); + }); + + connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), + [&] { UpdateMappingWithDefaults(); }); + + ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); + UpdateInputDevices(); + connect(ui->buttonRefreshDevices, &QPushButton::clicked, [&] { emit RefreshInputDevices(); }); timeout_timer->setSingleShot(true); connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); @@ -416,20 +424,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } }); - controller_color_buttons = { - ui->left_body_button, - ui->left_buttons_button, - ui->right_body_button, - ui->right_buttons_button, - }; - - 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)); }); - } - LoadConfiguration(); - resize(0, 0); // TODO(wwylele): enable this when we actually emulate it ui->buttonHome->setEnabled(false); @@ -438,27 +433,43 @@ 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[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; + } - std::array<u32, 4> colors{}; - std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(), - [](QColor color) { return color.rgb(); }); + player.controller_type = + static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex()); + player.connected = ui->groupConnectedController->isChecked(); - 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]; + // Player 2-8 + if (player_index != 0) { + UpdateController(player.controller_type, player_index, player.connected); + return; + } + + // Player 1 and Handheld + auto& handheld = Settings::values.players[HANDHELD_INDEX]; + // If Handheld is selected, copy all the settings from Player 1 to Handheld. + if (player.controller_type == Settings::ControllerType::Handheld) { + handheld = player; + handheld.connected = ui->groupConnectedController->isChecked(); + player.connected = false; // Disconnect Player 1 + } else { + player.connected = ui->groupConnectedController->isChecked(); + handheld.connected = false; // Disconnect Handheld + } + + UpdateController(player.controller_type, player_index, player.connected); + UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); } void ConfigureInputPlayer::changeEvent(QEvent* event) { @@ -466,24 +477,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[player_index]; if (debug) { std::transform(Settings::values.debug_pad_buttons.begin(), Settings::values.debug_pad_buttons.end(), buttons_param.begin(), @@ -492,56 +495,53 @@ 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(Settings::values.players[player_index].analogs.begin(), - Settings::values.players[player_index].analogs.end(), analogs_param.begin(), + std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(), [](const std::string& str) { return Common::ParamPackage(str); }); } - UpdateButtonLabels(); + UpdateUI(); - 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, - }; - - std::transform(colors.begin(), colors.end(), controller_colors.begin(), - [](u32 rgb) { return QColor::fromRgb(rgb); }); + ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type)); + ui->groupConnectedController->setChecked( + player.connected || + (player_index == 0 && Settings::values.players[HANDHELD_INDEX].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::UpdateInputDevices() { + input_devices = InputCommon::GetInputDevices(); + ui->comboDevices->clear(); + for (auto device : input_devices) { + ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {}); } } void ConfigureInputPlayer::RestoreDefaults() { - for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { + // Reset Buttons + 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++) { + // Reset Analogs + 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]); + SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); } } - - UpdateButtonLabels(); - ApplyConfiguration(); + UpdateUI(); + UpdateInputDevices(); + ui->comboControllerType->setCurrentIndex(0); } 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()) { continue; @@ -550,8 +550,8 @@ void ConfigureInputPlayer::ClearAll() { 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()) { continue; @@ -561,17 +561,17 @@ void ConfigureInputPlayer::ClearAll() { } } - UpdateButtonLabels(); - ApplyConfiguration(); + 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 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,52 +581,75 @@ 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")); + 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); - } - - 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); - } - - 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%")); - } + const bool is_controller = + param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad"; + + 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); } + + 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::UpdateMappingWithDefaults() { + if (ui->comboDevices->currentIndex() < 2) { + return; + } + const auto& device = input_devices[ui->comboDevices->currentIndex()]; + auto button_mapping = InputCommon::GetButtonMappingForDevice(device); + auto analog_mapping = InputCommon::GetAnalogMappingForDevice(device); + for (int i = 0; i < buttons_param.size(); ++i) { + buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)]; + } + for (int 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]")); + 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. If the user filtered to a + // controller, then they don't want keyboard input + want_keyboard_keys = ui->comboDevices->currentIndex() < 2; input_setter = new_input_setter; @@ -636,20 +659,17 @@ void ConfigureInputPlayer::HandleClick( poller->Start(); } - grabKeyboard(); - grabMouse(); if (type == InputCommon::Polling::DeviceType::Button) { InputCommon::GetGCButtons()->BeginConfiguration(); } else { InputCommon::GetGCAnalogs()->BeginConfiguration(); } - timeout_timer->start(5000); // Cancel after 5 seconds - poll_timer->start(200); // Check for new inputs every 200ms + + 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) { @@ -663,7 +683,7 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, (*input_setter)(params); } - UpdateButtonLabels(); + UpdateUI(); input_setter = std::nullopt; } @@ -683,3 +703,114 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { } SetPollingResult({}, true); } + +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) + QString stylesheet{}; + switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { + case Settings::ControllerType::ProController: + stylesheet = QStringLiteral("image: url(:/controller/pro_controller%0)"); + break; + case Settings::ControllerType::DualJoyconDetached: + stylesheet = QStringLiteral("image: url(:/controller/dual_joycon%0)"); + break; + case Settings::ControllerType::LeftJoycon: + stylesheet = QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)"); + break; + case Settings::ControllerType::RightJoycon: + stylesheet = QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)"); + break; + case Settings::ControllerType::Handheld: + stylesheet = QStringLiteral("image: url(:/controller/handheld%0)"); + break; + default: + break; + } + + const QString theme = [this] { + 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::DualJoyconDetached; + } + + // 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::vector<QWidget*> 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: + layout_hidden = { + ui->buttonShoulderButtonsSLSR, + ui->horizontalSpacerShoulderButtonsWidget2, + }; + break; + 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::showEvent(QShowEvent* event) { + if (bottom_row == nullptr) { + return; + } + QWidget::showEvent(event); + ui->main->addWidget(bottom_row); +} + +void ConfigureInputPlayer::ConnectPlayer(bool connected) { + ui->groupConnectedController->setChecked(connected); +} diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 95afa5375..a86db8200 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -15,11 +15,17 @@ #include "common/param_package.h" #include "core/settings.h" #include "ui_configure_input.h" +#include "yuzu/uisettings.h" +class QCheckBox; class QKeyEvent; +class QLabel; class QPushButton; +class QSlider; +class QSpinBox; class QString; class QTimer; +class QWidget; namespace InputCommon::Polling { class DevicePoller; @@ -30,43 +36,73 @@ 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, + bool debug = false); ~ConfigureInputPlayer() override; - /// Save all button configurations to settings file + /// Save all button configurations to settings file. void ApplyConfiguration(); + /// Update the input devices combobox. + void UpdateInputDevices(); + + /// Restore all buttons to their default values. + void RestoreDefaults(); + + /// Clear all input configuration. + void ClearAll(); + + /// Set the connection state checkbox (used to sync state). + void ConnectPlayer(bool connected); + +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(); + +protected: + void showEvent(QShowEvent* event) override; + private: void changeEvent(QEvent* event) override; void RetranslateUI(); - void OnControllerButtonClick(int i); - /// Load configuration settings. void LoadConfiguration(); - /// Restore all buttons to their default values. - void RestoreDefaults(); - /// Clear all input configuration - void ClearAll(); - - /// Update UI to reflect current configuration. - void UpdateButtonLabels(); /// 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); /// Handle key press events. void keyPressEvent(QKeyEvent* event) override; + /// Update UI to reflect current configuration. + void UpdateUI(); + + /// Update the controller selection combobox + void UpdateControllerCombobox(); + + /// Update the current controller icon. + void UpdateControllerIcon(); + + /// Hides and disables controller settings based on the current controller type. + void UpdateControllerAvailableButtons(); + + /// Gets the default controller mapping for this device and auto configures the input to match. + void UpdateMappingWithDefaults(); + std::unique_ptr<Ui::ConfigureInputPlayer> ui; std::size_t player_index; @@ -75,32 +111,38 @@ private: 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; - static constexpr int ANALOG_SUB_BUTTONS_NUM = 5; + static constexpr int ANALOG_SUB_BUTTONS_NUM = 4; + // Adds room for two extra push buttons LStick Modifier and RStick Modifier. + static constexpr int BUTTON_MAP_COUNT = Settings::NativeButton::NumButtons + 2; /// 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; + std::array<QPushButton*, BUTTON_MAP_COUNT> button_map; + /// Extra buttons for the modifiers. + Common::ParamPackage lstick_mod; + Common::ParamPackage rstick_mod; - /// 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; + 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; @@ -110,6 +152,12 @@ private: /// keyboard events are ignored. bool want_keyboard_keys = false; - std::array<QPushButton*, 4> controller_color_buttons; - std::array<QColor, 4> controller_colors; + /// List of physical devices users can map with. If a SDL backed device is selected, then you + /// can usue this device to get a default mapping. + std::vector<Common::ParamPackage> input_devices; + + /// 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..eb826a935 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -1,1243 +1,2974 @@ <?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"> <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"> <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</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>24</width> + <height>22</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>24</width> + <height>22</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> + <widget class="QComboBox" name="comboProfiles"/> </item> <item> - <widget class="QPushButton" name="buttonDpadUp"> - <property name="text"> - <string/> + <widget class="QPushButton" name="buttonProfilesSave"> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</string> </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"> <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>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>55</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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> + <item> + <spacer name="horizontalSpacerMiscButtons1"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item> + <spacer name="horizontalSpacerMiscButtons4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>0</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">min-width: 55px;</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>55</width> + <height>21</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</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_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_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp index 5bcf5ffa8..95e1ae873 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.cpp +++ b/src/yuzu/configuration/configure_mouse_advanced.cpp @@ -205,15 +205,11 @@ void ConfigureMouseAdvanced::HandleClick( poller->Start(); } - grabKeyboard(); - grabMouse(); - timeout_timer->start(5000); // Cancel after 5 seconds - poll_timer->start(200); // Check for new inputs every 200ms + 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) { diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui index 08245ecf0..74552fdbd 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: 55px; +}</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>57</width> <height>0</height> </size> </property> - <property name="text"> - <string/> + <property name="maximumSize"> + <size> + <width>16777215</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>57</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>57</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</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>57</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</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/> diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp index a51175f36..37499fc85 100644 --- a/src/yuzu/uisettings.cpp +++ b/src/yuzu/uisettings.cpp @@ -16,4 +16,5 @@ const Themes themes{{ }}; Values values = {}; + } // namespace UISettings diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 2d2e82f15..533815098 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -13,6 +13,7 @@ #include <QStringList> #include <QVector> #include "common/common_types.h" +#include "core/settings.h" namespace UISettings { @@ -87,9 +88,6 @@ struct Values { // logging bool show_console; - // Controllers - int profile_index; - // Game List bool show_add_ons; uint32_t icon_size; @@ -100,6 +98,7 @@ struct Values { }; extern Values values; + } // namespace UISettings Q_DECLARE_METATYPE(UISettings::GameDir*); diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 8a63fd191..e9f1c6500 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -288,6 +288,8 @@ void Config::ReadValues() { Settings::values.debug_pad_analogs[i] = default_param; } + Settings::values.vibration_enabled = + sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true); Settings::values.touchscreen.enabled = sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); Settings::values.touchscreen.device = diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp index 74022af23..aaf59129a 100644 --- a/src/yuzu_tester/config.cpp +++ b/src/yuzu_tester/config.cpp @@ -75,6 +75,7 @@ void Config::ReadValues() { Settings::values.debug_pad_analogs[i] = ""; } + Settings::values.vibration_enabled = true; Settings::values.touchscreen.enabled = ""; Settings::values.touchscreen.device = ""; Settings::values.touchscreen.finger = 0; |