Merge pull request #5339 from german77/interactive
Settings: Make settings controller image change with controller input
21
dist/icons/controller/controller.qrc
vendored
@ -1,26 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="controller">
|
||||
<file alias="dual_joycon">dual_joycon.png</file>
|
||||
<file alias="dual_joycon_dark">dual_joycon_dark.png</file>
|
||||
<file alias="dual_joycon_midnight">dual_joycon_midnight.png</file>
|
||||
<file alias="handheld">handheld.png</file>
|
||||
<file alias="handheld_dark">handheld_dark.png</file>
|
||||
<file alias="handheld_midnight">handheld_midnight.png</file>
|
||||
<file alias="pro_controller">pro_controller.png</file>
|
||||
<file alias="pro_controller_dark">pro_controller_dark.png</file>
|
||||
<file alias="pro_controller_midnight">pro_controller_midnight.png</file>
|
||||
<file alias="single_joycon_left">single_joycon_left.png</file>
|
||||
<file alias="single_joycon_left_dark">single_joycon_left_dark.png</file>
|
||||
<file alias="single_joycon_left_midnight">single_joycon_left_midnight.png</file>
|
||||
<file alias="single_joycon_right">single_joycon_right.png</file>
|
||||
<file alias="single_joycon_right_dark">single_joycon_right_dark.png</file>
|
||||
<file alias="single_joycon_right_midnight">single_joycon_right_midnight.png</file>
|
||||
<file alias="single_joycon_left_vertical">single_joycon_left_vertical.png</file>
|
||||
<file alias="single_joycon_left_vertical_dark">single_joycon_left_vertical_dark.png</file>
|
||||
<file alias="single_joycon_left_vertical_midnight">single_joycon_left_vertical_midnight.png</file>
|
||||
<file alias="single_joycon_right_vertical">single_joycon_right_vertical.png</file>
|
||||
<file alias="single_joycon_right_vertical_dark">single_joycon_right_vertical_dark.png</file>
|
||||
<file alias="single_joycon_right_vertical_midnight">single_joycon_right_vertical_midnight.png</file>
|
||||
<file alias="applet_dual_joycon">applet_dual_joycon.png</file>
|
||||
<file alias="applet_dual_joycon_dark">applet_dual_joycon_dark.png</file>
|
||||
<file alias="applet_dual_joycon_midnight">applet_dual_joycon_midnight.png</file>
|
||||
|
BIN
dist/icons/controller/dual_joycon.png
vendored
Before Width: | Height: | Size: 36 KiB |
BIN
dist/icons/controller/dual_joycon_dark.png
vendored
Before Width: | Height: | Size: 35 KiB |
BIN
dist/icons/controller/dual_joycon_midnight.png
vendored
Before Width: | Height: | Size: 34 KiB |
BIN
dist/icons/controller/handheld.png
vendored
Before Width: | Height: | Size: 14 KiB |
BIN
dist/icons/controller/handheld_dark.png
vendored
Before Width: | Height: | Size: 13 KiB |
BIN
dist/icons/controller/handheld_midnight.png
vendored
Before Width: | Height: | Size: 13 KiB |
BIN
dist/icons/controller/pro_controller.png
vendored
Before Width: | Height: | Size: 36 KiB |
BIN
dist/icons/controller/pro_controller_dark.png
vendored
Before Width: | Height: | Size: 34 KiB |
BIN
dist/icons/controller/pro_controller_midnight.png
vendored
Before Width: | Height: | Size: 35 KiB |
BIN
dist/icons/controller/single_joycon_left.png
vendored
Before Width: | Height: | Size: 25 KiB |
BIN
dist/icons/controller/single_joycon_left_dark.png
vendored
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 23 KiB |
BIN
dist/icons/controller/single_joycon_right.png
vendored
Before Width: | Height: | Size: 28 KiB |
BIN
dist/icons/controller/single_joycon_right_dark.png
vendored
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB |
@ -21,6 +21,11 @@ enum class AnalogDirection : u8 {
|
||||
UP,
|
||||
DOWN,
|
||||
};
|
||||
struct AnalogProperties {
|
||||
float deadzone;
|
||||
float range;
|
||||
float threshold;
|
||||
};
|
||||
|
||||
/// An abstract class template for an input device (a button, an analog input, etc.).
|
||||
template <typename StatusType>
|
||||
@ -30,6 +35,12 @@ public:
|
||||
virtual StatusType GetStatus() const {
|
||||
return {};
|
||||
}
|
||||
virtual StatusType GetRawStatus() const {
|
||||
return GetStatus();
|
||||
}
|
||||
virtual AnalogProperties GetAnalogProperties() const {
|
||||
return {};
|
||||
}
|
||||
virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
|
||||
return {};
|
||||
}
|
||||
|
@ -139,6 +139,10 @@ public:
|
||||
static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
|
||||
}
|
||||
|
||||
Input::AnalogProperties GetAnalogProperties() const override {
|
||||
return {modifier_scale, 1.0f, 0.5f};
|
||||
}
|
||||
|
||||
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
|
||||
switch (direction) {
|
||||
case Input::AnalogDirection::RIGHT:
|
||||
|
@ -185,6 +185,16 @@ public:
|
||||
return {0.0f, 0.0f};
|
||||
}
|
||||
|
||||
std::tuple<float, float> GetRawStatus() const override {
|
||||
const float x = GetAxis(axis_x);
|
||||
const float y = GetAxis(axis_y);
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
Input::AnalogProperties GetAnalogProperties() const override {
|
||||
return {deadzone, range, 0.5f};
|
||||
}
|
||||
|
||||
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
|
||||
const auto [x, y] = GetStatus();
|
||||
const float directional_deadzone = 0.5f;
|
||||
|
@ -106,6 +106,16 @@ public:
|
||||
return {0.0f, 0.0f};
|
||||
}
|
||||
|
||||
std::tuple<float, float> GetRawStatus() const override {
|
||||
const float x = GetAxis(axis_x);
|
||||
const float y = GetAxis(axis_y);
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
Input::AnalogProperties GetAnalogProperties() const override {
|
||||
return {deadzone, range, 0.5f};
|
||||
}
|
||||
|
||||
private:
|
||||
const u32 button;
|
||||
const u32 axis_x;
|
||||
|
@ -377,6 +377,16 @@ public:
|
||||
return {};
|
||||
}
|
||||
|
||||
std::tuple<float, float> GetRawStatus() const override {
|
||||
const float x = joystick->GetAxis(axis_x, range);
|
||||
const float y = joystick->GetAxis(axis_y, range);
|
||||
return {x, -y};
|
||||
}
|
||||
|
||||
Input::AnalogProperties GetAnalogProperties() const override {
|
||||
return {deadzone, range, 0.5f};
|
||||
}
|
||||
|
||||
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
|
||||
const auto [x, y] = GetStatus();
|
||||
const float directional_deadzone = 0.5f;
|
||||
|
@ -71,6 +71,8 @@ add_executable(yuzu
|
||||
configuration/configure_input_player.cpp
|
||||
configuration/configure_input_player.h
|
||||
configuration/configure_input_player.ui
|
||||
configuration/configure_input_player_widget.cpp
|
||||
configuration/configure_input_player_widget.h
|
||||
configuration/configure_input_profile_dialog.cpp
|
||||
configuration/configure_input_profile_dialog.h
|
||||
configuration/configure_input_profile_dialog.ui
|
||||
@ -115,6 +117,8 @@ add_executable(yuzu
|
||||
configuration/input_profiles.h
|
||||
debugger/console.cpp
|
||||
debugger/console.h
|
||||
debugger/controller.cpp
|
||||
debugger/controller.h
|
||||
debugger/profiler.cpp
|
||||
debugger/profiler.h
|
||||
debugger/wait_tree.cpp
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "ui_configure_input_player.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_input_player.h"
|
||||
#include "yuzu/configuration/configure_input_player_widget.h"
|
||||
#include "yuzu/configuration/configure_vibration.h"
|
||||
#include "yuzu/configuration/input_profiles.h"
|
||||
#include "yuzu/util/limitable_input_dialog.h"
|
||||
@ -254,11 +255,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
|
||||
analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
|
||||
|
||||
const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param,
|
||||
int default_val, InputCommon::Polling::DeviceType type) {
|
||||
const auto ConfigureButtonClick = [&](QPushButton* button, std::size_t button_id,
|
||||
Common::ParamPackage* param, int default_val,
|
||||
InputCommon::Polling::DeviceType type) {
|
||||
connect(button, &QPushButton::clicked, [=, this] {
|
||||
HandleClick(
|
||||
button,
|
||||
button, button_id,
|
||||
[=, this](Common::ParamPackage params) {
|
||||
// Workaround for ZL & ZR for analog triggers like on XBOX
|
||||
// controllers. Analog triggers (from controllers like the XBOX
|
||||
@ -286,12 +288,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
continue;
|
||||
}
|
||||
|
||||
ConfigureButtonClick(button_map[button_id], &buttons_param[button_id],
|
||||
ConfigureButtonClick(button_map[button_id], button_id, &buttons_param[button_id],
|
||||
Config::default_buttons[button_id],
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
|
||||
button->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
connect(button, &QPushButton::customContextMenuRequested,
|
||||
[=, this](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
@ -300,6 +301,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
button_map[button_id]->setText(tr("[not set]"));
|
||||
});
|
||||
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
|
||||
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
|
||||
});
|
||||
}
|
||||
|
||||
@ -309,7 +311,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
continue;
|
||||
}
|
||||
|
||||
ConfigureButtonClick(motion_map[motion_id], &motions_param[motion_id],
|
||||
ConfigureButtonClick(motion_map[motion_id], motion_id, &motions_param[motion_id],
|
||||
Config::default_motions[motion_id],
|
||||
InputCommon::Polling::DeviceType::Motion);
|
||||
|
||||
@ -348,7 +350,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
}
|
||||
}
|
||||
HandleClick(
|
||||
analog_map_buttons[analog_id][sub_button_id],
|
||||
analog_map_buttons[analog_id][sub_button_id], analog_id,
|
||||
[=, this](const Common::ParamPackage& params) {
|
||||
SetAnalogParam(params, analogs_param[analog_id],
|
||||
analog_sub_buttons[sub_button_id]);
|
||||
@ -358,41 +360,43 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
|
||||
analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
connect(analog_button, &QPushButton::customContextMenuRequested,
|
||||
[=, this](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
analogs_param[analog_id].Clear();
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
|
||||
});
|
||||
context_menu.addAction(tr("Invert axis"), [&] {
|
||||
if (sub_button_id == 2 || sub_button_id == 3) {
|
||||
const bool invert_value =
|
||||
analogs_param[analog_id].Get("invert_x", "+") == "-";
|
||||
const std::string invert_str = invert_value ? "+" : "-";
|
||||
analogs_param[analog_id].Set("invert_x", invert_str);
|
||||
}
|
||||
if (sub_button_id == 0 || sub_button_id == 1) {
|
||||
const bool invert_value =
|
||||
analogs_param[analog_id].Get("invert_y", "+") == "-";
|
||||
const std::string invert_str = invert_value ? "+" : "-";
|
||||
analogs_param[analog_id].Set("invert_y", invert_str);
|
||||
}
|
||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
|
||||
++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_button, &QPushButton::customContextMenuRequested,
|
||||
[=, this](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
analogs_param[analog_id].Clear();
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
|
||||
});
|
||||
context_menu.addAction(tr("Invert axis"), [&] {
|
||||
if (sub_button_id == 2 || sub_button_id == 3) {
|
||||
const bool invert_value =
|
||||
analogs_param[analog_id].Get("invert_x", "+") == "-";
|
||||
const std::string invert_str = invert_value ? "+" : "-";
|
||||
analogs_param[analog_id].Set("invert_x", invert_str);
|
||||
}
|
||||
if (sub_button_id == 0 || sub_button_id == 1) {
|
||||
const bool invert_value =
|
||||
analogs_param[analog_id].Get("invert_y", "+") == "-";
|
||||
const std::string invert_str = invert_value ? "+" : "-";
|
||||
analogs_param[analog_id].Set("invert_y", invert_str);
|
||||
}
|
||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
|
||||
++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));
|
||||
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle clicks for the modifier buttons as well.
|
||||
connect(analog_map_modifier_button[analog_id], &QPushButton::clicked, [=, this] {
|
||||
HandleClick(
|
||||
analog_map_modifier_button[analog_id],
|
||||
analog_map_modifier_button[analog_id], analog_id,
|
||||
[=, this](const Common::ParamPackage& params) {
|
||||
analogs_param[analog_id].Set("modifier", params.Serialize());
|
||||
},
|
||||
@ -416,12 +420,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
[=, this] {
|
||||
const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
|
||||
analogs_param[analog_id].Set("range", spinbox_value / 100.0f);
|
||||
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
|
||||
});
|
||||
|
||||
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);
|
||||
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
|
||||
});
|
||||
|
||||
connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] {
|
||||
@ -433,8 +439,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
}
|
||||
|
||||
// Player Connected checkbox
|
||||
connect(ui->groupConnectedController, &QGroupBox::toggled,
|
||||
[this](bool checked) { emit Connected(checked); });
|
||||
connect(ui->groupConnectedController, &QGroupBox::toggled, [this](bool checked) {
|
||||
emit Connected(checked);
|
||||
ui->controllerFrame->SetConnectedStatus(checked);
|
||||
});
|
||||
|
||||
if (player_index == 0) {
|
||||
connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
@ -553,6 +561,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
|
||||
// TODO(wwylele): enable this when we actually emulate it
|
||||
ui->buttonHome->setEnabled(false);
|
||||
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
|
||||
ui->controllerFrame->SetConnectedStatus(ui->groupConnectedController->isChecked());
|
||||
}
|
||||
|
||||
ConfigureInputPlayer::~ConfigureInputPlayer() = default;
|
||||
@ -875,6 +885,7 @@ void ConfigureInputPlayer::UpdateUI() {
|
||||
modifier_label->setVisible(!is_controller);
|
||||
modifier_slider->setVisible(!is_controller);
|
||||
range_groupbox->setVisible(is_controller);
|
||||
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
|
||||
}
|
||||
}
|
||||
|
||||
@ -991,8 +1002,8 @@ void ConfigureInputPlayer::UpdateControllerIcon() {
|
||||
return QString{};
|
||||
}
|
||||
}();
|
||||
|
||||
ui->controllerFrame->setStyleSheet(stylesheet.arg(theme));
|
||||
ui->controllerFrame->SetControllerType(
|
||||
GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()));
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
|
||||
@ -1129,7 +1140,8 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::HandleClick(
|
||||
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
|
||||
QPushButton* button, std::size_t button_id,
|
||||
std::function<void(const Common::ParamPackage&)> new_input_setter,
|
||||
InputCommon::Polling::DeviceType type) {
|
||||
if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
|
||||
button->setText(tr("Shake!"));
|
||||
@ -1173,6 +1185,12 @@ void ConfigureInputPlayer::HandleClick(
|
||||
input_subsystem->GetMouseTouch()->BeginConfiguration();
|
||||
}
|
||||
|
||||
if (type == InputCommon::Polling::DeviceType::Button) {
|
||||
ui->controllerFrame->BeginMappingButton(button_id);
|
||||
} else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) {
|
||||
ui->controllerFrame->BeginMappingAnalog(button_id);
|
||||
}
|
||||
|
||||
timeout_timer->start(2500); // Cancel after 2.5 seconds
|
||||
poll_timer->start(50); // Check for new inputs every 50ms
|
||||
}
|
||||
@ -1203,6 +1221,7 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
|
||||
|
||||
UpdateUI();
|
||||
UpdateInputDeviceCombobox();
|
||||
ui->controllerFrame->EndMapping();
|
||||
|
||||
input_setter = std::nullopt;
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ private:
|
||||
void LoadConfiguration();
|
||||
|
||||
/// Called when the button was pressed.
|
||||
void HandleClick(QPushButton* button,
|
||||
void HandleClick(QPushButton* button, std::size_t button_id,
|
||||
std::function<void(const Common::ParamPackage&)> new_input_setter,
|
||||
InputCommon::Polling::DeviceType type);
|
||||
|
||||
|
@ -1964,39 +1964,39 @@
|
||||
</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>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="PlayerControlPreview" 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>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="miscButtons">
|
||||
<property name="spacing">
|
||||
@ -3087,6 +3087,14 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>PlayerControlPreview</class>
|
||||
<extends>QFrame</extends>
|
||||
<header>yuzu/configuration/configure_input_player_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../dist/icons/controller/controller.qrc"/>
|
||||
</resources>
|
||||
|
2694
src/yuzu/configuration/configure_input_player_widget.cpp
Normal file
192
src/yuzu/configuration/configure_input_player_widget.h
Normal file
@ -0,0 +1,192 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <QFrame>
|
||||
#include <QPointer>
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
class QLabel;
|
||||
|
||||
using AnalogParam = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
|
||||
using ButtonParam = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
|
||||
|
||||
// Widget for representing controller animations
|
||||
class PlayerControlPreview : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PlayerControlPreview(QWidget* parent);
|
||||
~PlayerControlPreview() override;
|
||||
|
||||
void SetPlayerInput(std::size_t index, const ButtonParam& buttons_param,
|
||||
const AnalogParam& analogs_param);
|
||||
void SetPlayerInputRaw(std::size_t index, const Settings::ButtonsRaw buttons_,
|
||||
Settings::AnalogsRaw analogs_);
|
||||
void SetConnectedStatus(bool checked);
|
||||
void SetControllerType(Settings::ControllerType type);
|
||||
void BeginMappingButton(std::size_t button_id);
|
||||
void BeginMappingAnalog(std::size_t button_id);
|
||||
void EndMapping();
|
||||
void UpdateInput();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
enum class Direction : std::size_t {
|
||||
None,
|
||||
Up,
|
||||
Right,
|
||||
Down,
|
||||
Left,
|
||||
};
|
||||
|
||||
enum class Symbol {
|
||||
House,
|
||||
A,
|
||||
B,
|
||||
X,
|
||||
Y,
|
||||
L,
|
||||
R,
|
||||
C,
|
||||
SL,
|
||||
ZL,
|
||||
ZR,
|
||||
SR,
|
||||
};
|
||||
|
||||
struct AxisValue {
|
||||
QPointF value{};
|
||||
QPointF raw_value{};
|
||||
Input::AnalogProperties properties{};
|
||||
int size{};
|
||||
QPoint offset{};
|
||||
bool active{};
|
||||
};
|
||||
|
||||
struct LedPattern {
|
||||
bool position1;
|
||||
bool position2;
|
||||
bool position3;
|
||||
bool position4;
|
||||
};
|
||||
|
||||
struct ColorMapping {
|
||||
QColor outline{};
|
||||
QColor primary{};
|
||||
QColor left{};
|
||||
QColor right{};
|
||||
QColor button{};
|
||||
QColor button2{};
|
||||
QColor font{};
|
||||
QColor font2{};
|
||||
QColor highlight{};
|
||||
QColor highlight2{};
|
||||
QColor transparent{};
|
||||
QColor indicator{};
|
||||
QColor led_on{};
|
||||
QColor led_off{};
|
||||
QColor slider{};
|
||||
QColor slider_button{};
|
||||
QColor slider_arrow{};
|
||||
QColor deadzone{};
|
||||
};
|
||||
|
||||
static LedPattern GetColorPattern(std::size_t index, bool player_on);
|
||||
void UpdateColors();
|
||||
|
||||
// Draw controller functions
|
||||
void DrawHandheldController(QPainter& p, QPointF center);
|
||||
void DrawDualController(QPainter& p, QPointF center);
|
||||
void DrawLeftController(QPainter& p, QPointF center);
|
||||
void DrawRightController(QPainter& p, QPointF center);
|
||||
void DrawProController(QPainter& p, QPointF center);
|
||||
void DrawGCController(QPainter& p, QPointF center);
|
||||
|
||||
// Draw body functions
|
||||
void DrawHandheldBody(QPainter& p, QPointF center);
|
||||
void DrawDualBody(QPainter& p, QPointF center);
|
||||
void DrawLeftBody(QPainter& p, QPointF center);
|
||||
void DrawRightBody(QPainter& p, QPointF center);
|
||||
void DrawProBody(QPainter& p, QPointF center);
|
||||
void DrawGCBody(QPainter& p, QPointF center);
|
||||
|
||||
// Draw triggers functions
|
||||
void DrawProTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed);
|
||||
void DrawGCTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed);
|
||||
void DrawHandheldTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed);
|
||||
void DrawDualTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed);
|
||||
void DrawDualTriggersTopView(QPainter& p, QPointF center, bool left_pressed,
|
||||
bool right_pressed);
|
||||
void DrawDualZTriggersTopView(QPainter& p, QPointF center, bool left_pressed,
|
||||
bool right_pressed);
|
||||
void DrawLeftTriggers(QPainter& p, QPointF center, bool left_pressed);
|
||||
void DrawLeftZTriggers(QPainter& p, QPointF center, bool left_pressed);
|
||||
void DrawLeftTriggersTopView(QPainter& p, QPointF center, bool left_pressed);
|
||||
void DrawLeftZTriggersTopView(QPainter& p, QPointF center, bool left_pressed);
|
||||
void DrawRightTriggers(QPainter& p, QPointF center, bool right_pressed);
|
||||
void DrawRightZTriggers(QPainter& p, QPointF center, bool right_pressed);
|
||||
void DrawRightTriggersTopView(QPainter& p, QPointF center, bool right_pressed);
|
||||
void DrawRightZTriggersTopView(QPainter& p, QPointF center, bool right_pressed);
|
||||
|
||||
// Draw joystick functions
|
||||
void DrawJoystick(QPainter& p, QPointF center, float size, bool pressed);
|
||||
void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size, bool pressed);
|
||||
void DrawRawJoystick(QPainter& p, QPointF center, const QPointF value,
|
||||
const Input::AnalogProperties properties);
|
||||
void DrawProJoystick(QPainter& p, QPointF center, bool pressed);
|
||||
void DrawGCJoystick(QPainter& p, QPointF center, bool pressed);
|
||||
|
||||
// Draw button functions
|
||||
void DrawCircleButton(QPainter& p, QPointF center, bool pressed, float button_size);
|
||||
void DrawRoundButton(QPainter& p, QPointF center, bool pressed, float width, float height,
|
||||
Direction direction = Direction::None, float radius = 2);
|
||||
void DrawMinusButton(QPainter& p, QPointF center, bool pressed, int button_size);
|
||||
void DrawPlusButton(QPainter& p, QPointF center, bool pressed, int button_size);
|
||||
void DrawGCButtonX(QPainter& p, QPointF center, bool pressed);
|
||||
void DrawGCButtonY(QPainter& p, QPointF center, bool pressed);
|
||||
void DrawGCButtonZ(QPainter& p, QPointF center, bool pressed);
|
||||
void DrawArrowButtonOutline(QPainter& p, const QPointF center, float size = 1.0f);
|
||||
void DrawArrowButton(QPainter& p, QPointF center, Direction direction, bool pressed,
|
||||
float size = 1.0f);
|
||||
void DrawTriggerButton(QPainter& p, QPointF center, Direction direction, bool pressed);
|
||||
|
||||
// Draw icon functions
|
||||
void DrawSymbol(QPainter& p, QPointF center, Symbol symbol, float icon_size);
|
||||
void DrawArrow(QPainter& p, QPointF center, Direction direction, float size);
|
||||
|
||||
// Draw primitive types
|
||||
template <size_t N>
|
||||
void DrawPolygon(QPainter& p, const std::array<QPointF, N>& polygon);
|
||||
void DrawCircle(QPainter& p, QPointF center, float size);
|
||||
void DrawRectangle(QPainter& p, QPointF center, float width, float height);
|
||||
void DrawRoundRectangle(QPainter& p, QPointF center, float width, float height, float round);
|
||||
void DrawText(QPainter& p, QPointF center, float text_size, const QString& text);
|
||||
void SetTextFont(QPainter& p, float text_size,
|
||||
const QString& font_family = QStringLiteral("sans-serif"));
|
||||
|
||||
using ButtonArray =
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::BUTTON_NS_END>;
|
||||
using StickArray =
|
||||
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>;
|
||||
|
||||
bool mapping_active{};
|
||||
int blink_counter{};
|
||||
QColor button_color{};
|
||||
ColorMapping colors{};
|
||||
std::array<QColor, 4> led_color{};
|
||||
ButtonArray buttons{};
|
||||
StickArray sticks{};
|
||||
std::size_t player_index{};
|
||||
std::size_t button_mapping_index{Settings::NativeButton::BUTTON_NS_END};
|
||||
std::size_t analog_mapping_index{Settings::NativeAnalog::NUM_STICKS_HID};
|
||||
std::array<AxisValue, Settings::NativeAnalog::NUM_STICKS_HID> axis_values{};
|
||||
std::array<bool, Settings::NativeButton::NumButtons> button_values{};
|
||||
Settings::ControllerType controller_type{Settings::ControllerType::ProController};
|
||||
};
|
66
src/yuzu/debugger/controller.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QAction>
|
||||
#include <QLayout>
|
||||
#include <QString>
|
||||
#include "core/settings.h"
|
||||
#include "yuzu/configuration/configure_input_player_widget.h"
|
||||
#include "yuzu/debugger/controller.h"
|
||||
|
||||
ControllerDialog::ControllerDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) {
|
||||
setObjectName(QStringLiteral("Controller"));
|
||||
setWindowTitle(tr("Controller P1"));
|
||||
resize(500, 350);
|
||||
setMinimumSize(500, 350);
|
||||
// Remove the "?" button from the titlebar and enable the maximize button
|
||||
setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) |
|
||||
Qt::WindowMaximizeButtonHint);
|
||||
|
||||
widget = new PlayerControlPreview(this);
|
||||
refreshConfiguration();
|
||||
QLayout* layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(widget);
|
||||
setLayout(layout);
|
||||
|
||||
// Configure focus so that widget is focusable and the dialog automatically forwards focus to
|
||||
// it.
|
||||
setFocusProxy(widget);
|
||||
widget->setFocusPolicy(Qt::StrongFocus);
|
||||
widget->setFocus();
|
||||
}
|
||||
|
||||
void ControllerDialog::refreshConfiguration() {
|
||||
const auto& players = Settings::values.players.GetValue();
|
||||
constexpr std::size_t player = 0;
|
||||
widget->SetPlayerInputRaw(player, players[player].buttons, players[player].analogs);
|
||||
widget->SetConnectedStatus(players[player].connected);
|
||||
widget->SetControllerType(players[player].controller_type);
|
||||
}
|
||||
|
||||
QAction* ControllerDialog::toggleViewAction() {
|
||||
if (toggle_view_action == nullptr) {
|
||||
toggle_view_action = new QAction(windowTitle(), this);
|
||||
toggle_view_action->setCheckable(true);
|
||||
toggle_view_action->setChecked(isVisible());
|
||||
connect(toggle_view_action, &QAction::toggled, this, &ControllerDialog::setVisible);
|
||||
}
|
||||
|
||||
return toggle_view_action;
|
||||
}
|
||||
|
||||
void ControllerDialog::showEvent(QShowEvent* ev) {
|
||||
if (toggle_view_action) {
|
||||
toggle_view_action->setChecked(isVisible());
|
||||
}
|
||||
QWidget::showEvent(ev);
|
||||
}
|
||||
|
||||
void ControllerDialog::hideEvent(QHideEvent* ev) {
|
||||
if (toggle_view_action) {
|
||||
toggle_view_action->setChecked(isVisible());
|
||||
}
|
||||
QWidget::hideEvent(ev);
|
||||
}
|
31
src/yuzu/debugger/controller.h
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QAction;
|
||||
class QHideEvent;
|
||||
class QShowEvent;
|
||||
class PlayerControlPreview;
|
||||
|
||||
class ControllerDialog : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ControllerDialog(QWidget* parent = nullptr);
|
||||
|
||||
/// Returns a QAction that can be used to toggle visibility of this dialog.
|
||||
QAction* toggleViewAction();
|
||||
void refreshConfiguration();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* ev) override;
|
||||
void hideEvent(QHideEvent* ev) override;
|
||||
|
||||
private:
|
||||
QAction* toggle_view_action = nullptr;
|
||||
PlayerControlPreview* widget;
|
||||
};
|
@ -109,6 +109,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_dialog.h"
|
||||
#include "yuzu/debugger/console.h"
|
||||
#include "yuzu/debugger/controller.h"
|
||||
#include "yuzu/debugger/profiler.h"
|
||||
#include "yuzu/debugger/wait_tree.h"
|
||||
#include "yuzu/discord.h"
|
||||
@ -688,6 +689,11 @@ void GMainWindow::InitializeDebugWidgets() {
|
||||
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
|
||||
waitTreeWidget->hide();
|
||||
debug_menu->addAction(waitTreeWidget->toggleViewAction());
|
||||
|
||||
controller_dialog = new ControllerDialog(this);
|
||||
controller_dialog->hide();
|
||||
debug_menu->addAction(controller_dialog->toggleViewAction());
|
||||
|
||||
connect(this, &GMainWindow::EmulationStarting, waitTreeWidget,
|
||||
&WaitTreeWidget::OnEmulationStarting);
|
||||
connect(this, &GMainWindow::EmulationStopping, waitTreeWidget,
|
||||
@ -2336,6 +2342,7 @@ void GMainWindow::OnConfigure() {
|
||||
}
|
||||
|
||||
configure_dialog.ApplyConfiguration();
|
||||
controller_dialog->refreshConfiguration();
|
||||
InitializeHotkeys();
|
||||
if (UISettings::values.theme != old_theme) {
|
||||
UpdateUITheme();
|
||||
|
@ -27,6 +27,7 @@ class GRenderWindow;
|
||||
class LoadingScreen;
|
||||
class MicroProfileDialog;
|
||||
class ProfilerWidget;
|
||||
class ControllerDialog;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class QProgressDialog;
|
||||
@ -313,6 +314,7 @@ private:
|
||||
ProfilerWidget* profilerWidget;
|
||||
MicroProfileDialog* microProfileDialog;
|
||||
WaitTreeWidget* waitTreeWidget;
|
||||
ControllerDialog* controller_dialog;
|
||||
|
||||
QAction* actions_recent_files[max_recent_files_item];
|
||||
|
||||
|