Merge pull request #5339 from german77/interactive

Settings: Make settings controller image change with controller input
This commit is contained in:
bunnei 2021-02-07 20:53:46 -08:00 committed by GitHub
commit 0896089092
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 3143 additions and 96 deletions

View File

@ -1,26 +1,5 @@
<RCC> <RCC>
<qresource prefix="controller"> <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">applet_dual_joycon.png</file>
<file alias="applet_dual_joycon_dark">applet_dual_joycon_dark.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> <file alias="applet_dual_joycon_midnight">applet_dual_joycon_midnight.png</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -21,6 +21,11 @@ enum class AnalogDirection : u8 {
UP, UP,
DOWN, DOWN,
}; };
struct AnalogProperties {
float deadzone;
float range;
float threshold;
};
/// An abstract class template for an input device (a button, an analog input, etc.). /// An abstract class template for an input device (a button, an analog input, etc.).
template <typename StatusType> template <typename StatusType>
@ -30,6 +35,12 @@ public:
virtual StatusType GetStatus() const { virtual StatusType GetStatus() const {
return {}; return {};
} }
virtual StatusType GetRawStatus() const {
return GetStatus();
}
virtual AnalogProperties GetAnalogProperties() const {
return {};
}
virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const { virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
return {}; return {};
} }

View File

@ -139,6 +139,10 @@ public:
static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF)); 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 { bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
switch (direction) { switch (direction) {
case Input::AnalogDirection::RIGHT: case Input::AnalogDirection::RIGHT:

View File

@ -185,6 +185,16 @@ public:
return {0.0f, 0.0f}; 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 { bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
const auto [x, y] = GetStatus(); const auto [x, y] = GetStatus();
const float directional_deadzone = 0.5f; const float directional_deadzone = 0.5f;

View File

@ -106,6 +106,16 @@ public:
return {0.0f, 0.0f}; 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: private:
const u32 button; const u32 button;
const u32 axis_x; const u32 axis_x;

View File

@ -377,6 +377,16 @@ public:
return {}; 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 { bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
const auto [x, y] = GetStatus(); const auto [x, y] = GetStatus();
const float directional_deadzone = 0.5f; const float directional_deadzone = 0.5f;

View File

@ -71,6 +71,8 @@ add_executable(yuzu
configuration/configure_input_player.cpp configuration/configure_input_player.cpp
configuration/configure_input_player.h configuration/configure_input_player.h
configuration/configure_input_player.ui 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.cpp
configuration/configure_input_profile_dialog.h configuration/configure_input_profile_dialog.h
configuration/configure_input_profile_dialog.ui configuration/configure_input_profile_dialog.ui
@ -115,6 +117,8 @@ add_executable(yuzu
configuration/input_profiles.h configuration/input_profiles.h
debugger/console.cpp debugger/console.cpp
debugger/console.h debugger/console.h
debugger/controller.cpp
debugger/controller.h
debugger/profiler.cpp debugger/profiler.cpp
debugger/profiler.h debugger/profiler.h
debugger/wait_tree.cpp debugger/wait_tree.cpp

View File

@ -23,6 +23,7 @@
#include "ui_configure_input_player.h" #include "ui_configure_input_player.h"
#include "yuzu/configuration/config.h" #include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input_player.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/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h" #include "yuzu/configuration/input_profiles.h"
#include "yuzu/util/limitable_input_dialog.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_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange}; analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param, const auto ConfigureButtonClick = [&](QPushButton* button, std::size_t button_id,
int default_val, InputCommon::Polling::DeviceType type) { Common::ParamPackage* param, int default_val,
InputCommon::Polling::DeviceType type) {
connect(button, &QPushButton::clicked, [=, this] { connect(button, &QPushButton::clicked, [=, this] {
HandleClick( HandleClick(
button, button, button_id,
[=, this](Common::ParamPackage params) { [=, this](Common::ParamPackage params) {
// Workaround for ZL & ZR for analog triggers like on XBOX // Workaround for ZL & ZR for analog triggers like on XBOX
// controllers. Analog triggers (from controllers like the XBOX // controllers. Analog triggers (from controllers like the XBOX
@ -286,12 +288,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
continue; 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], Config::default_buttons[button_id],
InputCommon::Polling::DeviceType::Button); InputCommon::Polling::DeviceType::Button);
button->setContextMenuPolicy(Qt::CustomContextMenu); button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(button, &QPushButton::customContextMenuRequested, connect(button, &QPushButton::customContextMenuRequested,
[=, this](const QPoint& menu_location) { [=, this](const QPoint& menu_location) {
QMenu context_menu; QMenu context_menu;
@ -300,6 +301,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
button_map[button_id]->setText(tr("[not set]")); button_map[button_id]->setText(tr("[not set]"));
}); });
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); 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; 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], Config::default_motions[motion_id],
InputCommon::Polling::DeviceType::Motion); InputCommon::Polling::DeviceType::Motion);
@ -348,7 +350,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
} }
} }
HandleClick( HandleClick(
analog_map_buttons[analog_id][sub_button_id], analog_map_buttons[analog_id][sub_button_id], analog_id,
[=, this](const Common::ParamPackage& params) { [=, this](const Common::ParamPackage& params) {
SetAnalogParam(params, analogs_param[analog_id], SetAnalogParam(params, analogs_param[analog_id],
analog_sub_buttons[sub_button_id]); analog_sub_buttons[sub_button_id]);
@ -358,7 +360,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
analog_button->setContextMenuPolicy(Qt::CustomContextMenu); analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(analog_button, &QPushButton::customContextMenuRequested, connect(
analog_button, &QPushButton::customContextMenuRequested,
[=, this](const QPoint& menu_location) { [=, this](const QPoint& menu_location) {
QMenu context_menu; QMenu context_menu;
context_menu.addAction(tr("Clear"), [&] { context_menu.addAction(tr("Clear"), [&] {
@ -384,15 +387,16 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
analogs_param[analog_id], analog_sub_buttons[sub_button_id])); analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
} }
}); });
context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal( context_menu.exec(
menu_location)); 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. // Handle clicks for the modifier buttons as well.
connect(analog_map_modifier_button[analog_id], &QPushButton::clicked, [=, this] { connect(analog_map_modifier_button[analog_id], &QPushButton::clicked, [=, this] {
HandleClick( HandleClick(
analog_map_modifier_button[analog_id], analog_map_modifier_button[analog_id], analog_id,
[=, this](const Common::ParamPackage& params) { [=, this](const Common::ParamPackage& params) {
analogs_param[analog_id].Set("modifier", params.Serialize()); analogs_param[analog_id].Set("modifier", params.Serialize());
}, },
@ -416,12 +420,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
[=, this] { [=, this] {
const auto spinbox_value = analog_map_range_spinbox[analog_id]->value(); const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
analogs_param[analog_id].Set("range", spinbox_value / 100.0f); 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] { connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] {
const auto slider_value = analog_map_deadzone_slider[analog_id]->value(); const auto slider_value = analog_map_deadzone_slider[analog_id]->value();
analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value)); analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value));
analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); 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] { 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 // Player Connected checkbox
connect(ui->groupConnectedController, &QGroupBox::toggled, connect(ui->groupConnectedController, &QGroupBox::toggled, [this](bool checked) {
[this](bool checked) { emit Connected(checked); }); emit Connected(checked);
ui->controllerFrame->SetConnectedStatus(checked);
});
if (player_index == 0) { if (player_index == 0) {
connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), 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 // TODO(wwylele): enable this when we actually emulate it
ui->buttonHome->setEnabled(false); ui->buttonHome->setEnabled(false);
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
ui->controllerFrame->SetConnectedStatus(ui->groupConnectedController->isChecked());
} }
ConfigureInputPlayer::~ConfigureInputPlayer() = default; ConfigureInputPlayer::~ConfigureInputPlayer() = default;
@ -875,6 +885,7 @@ void ConfigureInputPlayer::UpdateUI() {
modifier_label->setVisible(!is_controller); modifier_label->setVisible(!is_controller);
modifier_slider->setVisible(!is_controller); modifier_slider->setVisible(!is_controller);
range_groupbox->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{}; return QString{};
} }
}(); }();
ui->controllerFrame->SetControllerType(
ui->controllerFrame->setStyleSheet(stylesheet.arg(theme)); GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()));
} }
void ConfigureInputPlayer::UpdateControllerAvailableButtons() { void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
@ -1129,7 +1140,8 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
} }
void ConfigureInputPlayer::HandleClick( 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) { InputCommon::Polling::DeviceType type) {
if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
button->setText(tr("Shake!")); button->setText(tr("Shake!"));
@ -1173,6 +1185,12 @@ void ConfigureInputPlayer::HandleClick(
input_subsystem->GetMouseTouch()->BeginConfiguration(); 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 timeout_timer->start(2500); // Cancel after 2.5 seconds
poll_timer->start(50); // Check for new inputs every 50ms poll_timer->start(50); // Check for new inputs every 50ms
} }
@ -1203,6 +1221,7 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
UpdateUI(); UpdateUI();
UpdateInputDeviceCombobox(); UpdateInputDeviceCombobox();
ui->controllerFrame->EndMapping();
input_setter = std::nullopt; input_setter = std::nullopt;
} }

View File

@ -106,7 +106,7 @@ private:
void LoadConfiguration(); void LoadConfiguration();
/// Called when the button was pressed. /// 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, std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type); InputCommon::Polling::DeviceType type);

View File

@ -1965,7 +1965,7 @@
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QFrame" name="controllerFrame"> <widget class="PlayerControlPreview" name="controllerFrame">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -3087,6 +3087,14 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>PlayerControlPreview</class>
<extends>QFrame</extends>
<header>yuzu/configuration/configure_input_player_widget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources> <resources>
<include location="../../../dist/icons/controller/controller.qrc"/> <include location="../../../dist/icons/controller/controller.qrc"/>
</resources> </resources>

File diff suppressed because it is too large Load Diff

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

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

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

View File

@ -109,6 +109,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "yuzu/configuration/config.h" #include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_dialog.h" #include "yuzu/configuration/configure_dialog.h"
#include "yuzu/debugger/console.h" #include "yuzu/debugger/console.h"
#include "yuzu/debugger/controller.h"
#include "yuzu/debugger/profiler.h" #include "yuzu/debugger/profiler.h"
#include "yuzu/debugger/wait_tree.h" #include "yuzu/debugger/wait_tree.h"
#include "yuzu/discord.h" #include "yuzu/discord.h"
@ -688,6 +689,11 @@ void GMainWindow::InitializeDebugWidgets() {
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
waitTreeWidget->hide(); waitTreeWidget->hide();
debug_menu->addAction(waitTreeWidget->toggleViewAction()); debug_menu->addAction(waitTreeWidget->toggleViewAction());
controller_dialog = new ControllerDialog(this);
controller_dialog->hide();
debug_menu->addAction(controller_dialog->toggleViewAction());
connect(this, &GMainWindow::EmulationStarting, waitTreeWidget, connect(this, &GMainWindow::EmulationStarting, waitTreeWidget,
&WaitTreeWidget::OnEmulationStarting); &WaitTreeWidget::OnEmulationStarting);
connect(this, &GMainWindow::EmulationStopping, waitTreeWidget, connect(this, &GMainWindow::EmulationStopping, waitTreeWidget,
@ -2336,6 +2342,7 @@ void GMainWindow::OnConfigure() {
} }
configure_dialog.ApplyConfiguration(); configure_dialog.ApplyConfiguration();
controller_dialog->refreshConfiguration();
InitializeHotkeys(); InitializeHotkeys();
if (UISettings::values.theme != old_theme) { if (UISettings::values.theme != old_theme) {
UpdateUITheme(); UpdateUITheme();

View File

@ -27,6 +27,7 @@ class GRenderWindow;
class LoadingScreen; class LoadingScreen;
class MicroProfileDialog; class MicroProfileDialog;
class ProfilerWidget; class ProfilerWidget;
class ControllerDialog;
class QLabel; class QLabel;
class QPushButton; class QPushButton;
class QProgressDialog; class QProgressDialog;
@ -313,6 +314,7 @@ private:
ProfilerWidget* profilerWidget; ProfilerWidget* profilerWidget;
MicroProfileDialog* microProfileDialog; MicroProfileDialog* microProfileDialog;
WaitTreeWidget* waitTreeWidget; WaitTreeWidget* waitTreeWidget;
ControllerDialog* controller_dialog;
QAction* actions_recent_files[max_recent_files_item]; QAction* actions_recent_files[max_recent_files_item];