yuzu-emu
/
yuzu-mainline
Archived
1
0
Fork 0

Make settings controller image change with controller input

This commit is contained in:
german 2021-01-12 21:09:59 -06:00
parent 61bf850f3d
commit 481cd86722
31 changed files with 2076 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

@ -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

@ -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

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,157 @@
// 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 SetConnectedStatus(bool checked);
void SetControllerType(Settings::ControllerType type);
void BeginMappingButton(std::size_t button_id);
void BeginMappingAnalog(std::size_t button_id);
void EndMapping();
protected:
void paintEvent(QPaintEvent* event) override;
private:
enum class Direction : std::size_t {
None,
Up,
Right,
Down,
Left,
};
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);
// 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);
// Draw triggers functions
void DrawProTriggers(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 DrawDualZTriggers(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 DrawRightTriggers(QPainter& p, QPointF center, bool right_pressed);
void DrawRightZTriggers(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);
// Draw button functions
void DrawCircleButton(QPainter& p, QPointF center, bool pressed, int 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 DrawArrowButton(QPainter& p, QPointF center, Direction direction, bool pressed);
// Draw icon functions
void DrawHouseIcon(QPainter& p, QPointF center, 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};
};