123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- // Copyright 2018 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include "DolphinQt/TAS/TASInputWindow.h"
- #include <cmath>
- #include <utility>
- #include <QApplication>
- #include <QCheckBox>
- #include <QEvent>
- #include <QGroupBox>
- #include <QHBoxLayout>
- #include <QLabel>
- #include <QShortcut>
- #include <QSlider>
- #include <QSpinBox>
- #include <QVBoxLayout>
- #include "Common/CommonTypes.h"
- #include "DolphinQt/Host.h"
- #include "DolphinQt/QtUtils/AspectRatioWidget.h"
- #include "DolphinQt/QtUtils/QueueOnObject.h"
- #include "DolphinQt/Resources.h"
- #include "DolphinQt/TAS/StickWidget.h"
- #include "DolphinQt/TAS/TASCheckBox.h"
- #include "DolphinQt/TAS/TASSlider.h"
- #include "DolphinQt/TAS/TASSpinBox.h"
- #include "InputCommon/ControllerEmu/ControllerEmu.h"
- #include "InputCommon/ControllerEmu/StickGate.h"
- void InputOverrider::AddFunction(std::string_view group_name, std::string_view control_name,
- OverrideFunction function)
- {
- m_functions.emplace(std::make_pair(group_name, control_name), std::move(function));
- }
- ControllerEmu::InputOverrideFunction InputOverrider::GetInputOverrideFunction() const
- {
- return [this](std::string_view group_name, std::string_view control_name,
- ControlState controller_state) {
- const auto it = m_functions.find(std::make_pair(group_name, control_name));
- return it != m_functions.end() ? it->second(controller_state) : std::nullopt;
- };
- }
- TASInputWindow::TASInputWindow(QWidget* parent) : QDialog(parent)
- {
- setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
- setWindowIcon(Resources::GetAppIcon());
- QGridLayout* settings_layout = new QGridLayout;
- m_use_controller = new QCheckBox(tr("Enable Controller Inpu&t"));
- m_use_controller->setToolTip(tr("Warning: Analog inputs may reset to controller values at "
- "random. In some cases this can be fixed by adding a deadzone."));
- settings_layout->addWidget(m_use_controller, 0, 0, 1, 2);
- QLabel* turbo_press_label = new QLabel(tr("Duration of Turbo Button Press (frames):"));
- m_turbo_press_frames = new QSpinBox();
- m_turbo_press_frames->setMinimum(1);
- settings_layout->addWidget(turbo_press_label, 1, 0);
- settings_layout->addWidget(m_turbo_press_frames, 1, 1);
- QLabel* turbo_release_label = new QLabel(tr("Duration of Turbo Button Release (frames):"));
- m_turbo_release_frames = new QSpinBox();
- m_turbo_release_frames->setMinimum(1);
- settings_layout->addWidget(turbo_release_label, 2, 0);
- settings_layout->addWidget(m_turbo_release_frames, 2, 1);
- m_settings_box = new QGroupBox(tr("Settings"));
- m_settings_box->setLayout(settings_layout);
- }
- int TASInputWindow::GetTurboPressFrames() const
- {
- return m_turbo_press_frames->value();
- }
- int TASInputWindow::GetTurboReleaseFrames() const
- {
- return m_turbo_release_frames->value();
- }
- TASCheckBox* TASInputWindow::CreateButton(const QString& text, std::string_view group_name,
- std::string_view control_name, InputOverrider* overrider)
- {
- TASCheckBox* checkbox = new TASCheckBox(text, this);
- overrider->AddFunction(group_name, control_name, [this, checkbox](ControlState controller_state) {
- return GetButton(checkbox, controller_state);
- });
- return checkbox;
- }
- QGroupBox* TASInputWindow::CreateStickInputs(const QString& text, std::string_view group_name,
- InputOverrider* overrider, int min_x, int min_y,
- int max_x, int max_y, Qt::Key x_shortcut_key,
- Qt::Key y_shortcut_key)
- {
- const QKeySequence x_shortcut_key_sequence = QKeySequence(Qt::ALT | x_shortcut_key);
- const QKeySequence y_shortcut_key_sequence = QKeySequence(Qt::ALT | y_shortcut_key);
- auto* box =
- new QGroupBox(QStringLiteral("%1 (%2/%3)")
- .arg(text, x_shortcut_key_sequence.toString(QKeySequence::NativeText),
- y_shortcut_key_sequence.toString(QKeySequence::NativeText)));
- const int x_default = static_cast<int>(std::round(max_x / 2.));
- const int y_default = static_cast<int>(std::round(max_y / 2.));
- auto* x_layout = new QHBoxLayout;
- TASSpinBox* x_value = CreateSliderValuePair(x_layout, x_default, max_x, x_shortcut_key_sequence,
- Qt::Horizontal, box);
- auto* y_layout = new QVBoxLayout;
- TASSpinBox* y_value =
- CreateSliderValuePair(y_layout, y_default, max_y, y_shortcut_key_sequence, Qt::Vertical, box);
- y_value->setMaximumWidth(60);
- auto* visual = new StickWidget(this, max_x, max_y);
- visual->SetX(x_default);
- visual->SetY(y_default);
- connect(x_value, &QSpinBox::valueChanged, visual, &StickWidget::SetX);
- connect(y_value, &QSpinBox::valueChanged, visual, &StickWidget::SetY);
- connect(visual, &StickWidget::ChangedX, x_value, &QSpinBox::setValue);
- connect(visual, &StickWidget::ChangedY, y_value, &QSpinBox::setValue);
- auto* visual_ar = new AspectRatioWidget(visual, max_x, max_y);
- auto* visual_layout = new QHBoxLayout;
- visual_layout->addWidget(visual_ar);
- visual_layout->addLayout(y_layout);
- auto* layout = new QVBoxLayout;
- layout->addLayout(x_layout);
- layout->addLayout(visual_layout);
- box->setLayout(layout);
- overrider->AddFunction(group_name, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE,
- [this, x_value, x_default, min_x, max_x](ControlState controller_state) {
- return GetSpinBox(x_value, x_default, min_x, max_x, controller_state);
- });
- overrider->AddFunction(group_name, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE,
- [this, y_value, y_default, min_y, max_y](ControlState controller_state) {
- return GetSpinBox(y_value, y_default, min_y, max_y, controller_state);
- });
- return box;
- }
- QBoxLayout* TASInputWindow::CreateSliderValuePairLayout(
- const QString& text, std::string_view group_name, std::string_view control_name,
- InputOverrider* overrider, int zero, int default_, int min, int max, Qt::Key shortcut_key,
- QWidget* shortcut_widget, std::optional<ControlState> scale)
- {
- const QKeySequence shortcut_key_sequence = QKeySequence(Qt::ALT | shortcut_key);
- auto* label = new QLabel(QStringLiteral("%1 (%2)").arg(
- text, shortcut_key_sequence.toString(QKeySequence::NativeText)));
- QBoxLayout* layout = new QHBoxLayout;
- layout->addWidget(label);
- CreateSliderValuePair(group_name, control_name, overrider, layout, zero, default_, min, max,
- shortcut_key_sequence, Qt::Horizontal, shortcut_widget, scale);
- return layout;
- }
- TASSpinBox* TASInputWindow::CreateSliderValuePair(
- std::string_view group_name, std::string_view control_name, InputOverrider* overrider,
- QBoxLayout* layout, int zero, int default_, int min, int max,
- QKeySequence shortcut_key_sequence, Qt::Orientation orientation, QWidget* shortcut_widget,
- std::optional<ControlState> scale)
- {
- TASSpinBox* value = CreateSliderValuePair(layout, default_, max, shortcut_key_sequence,
- orientation, shortcut_widget);
- InputOverrider::OverrideFunction func;
- if (scale)
- {
- func = [this, value, zero, scale](ControlState controller_state) {
- return GetSpinBox(value, zero, controller_state, *scale);
- };
- }
- else
- {
- func = [this, value, zero, min, max](ControlState controller_state) {
- return GetSpinBox(value, zero, min, max, controller_state);
- };
- }
- overrider->AddFunction(group_name, control_name, std::move(func));
- return value;
- }
- // The shortcut_widget argument needs to specify the container widget that will be hidden/shown.
- // This is done to avoid ambigous shortcuts
- TASSpinBox* TASInputWindow::CreateSliderValuePair(QBoxLayout* layout, int default_, int max,
- QKeySequence shortcut_key_sequence,
- Qt::Orientation orientation,
- QWidget* shortcut_widget)
- {
- auto* value = new TASSpinBox();
- value->setRange(0, 99999);
- value->setValue(default_);
- connect(value, &QSpinBox::valueChanged, [value, max](int i) {
- if (i > max)
- value->setValue(max);
- });
- auto* slider = new TASSlider(default_, orientation);
- slider->setRange(0, max);
- slider->setValue(default_);
- slider->setFocusPolicy(Qt::ClickFocus);
- connect(slider, &QSlider::valueChanged, value, &QSpinBox::setValue);
- connect(value, &QSpinBox::valueChanged, slider, &QSlider::setValue);
- auto* shortcut = new QShortcut(shortcut_key_sequence, shortcut_widget);
- connect(shortcut, &QShortcut::activated, [value] {
- value->setFocus();
- value->selectAll();
- });
- layout->addWidget(slider);
- layout->addWidget(value);
- if (orientation == Qt::Vertical)
- layout->setAlignment(slider, Qt::AlignRight);
- return value;
- }
- std::optional<ControlState> TASInputWindow::GetButton(TASCheckBox* checkbox,
- ControlState controller_state)
- {
- const bool pressed = std::llround(controller_state) > 0;
- if (m_use_controller->isChecked())
- checkbox->OnControllerValueChanged(pressed);
- return checkbox->GetValue() ? 1.0 : 0.0;
- }
- std::optional<ControlState> TASInputWindow::GetSpinBox(TASSpinBox* spin, int zero, int min, int max,
- ControlState controller_state)
- {
- const int controller_value =
- ControllerEmu::EmulatedController::MapFloat<int>(controller_state, zero, 0, max);
- if (m_use_controller->isChecked())
- spin->OnControllerValueChanged(controller_value);
- return ControllerEmu::EmulatedController::MapToFloat<ControlState, int>(spin->GetValue(), zero,
- min, max);
- }
- std::optional<ControlState> TASInputWindow::GetSpinBox(TASSpinBox* spin, int zero,
- ControlState controller_state,
- ControlState scale)
- {
- const int controller_value = static_cast<int>(std::llround(controller_state * scale + zero));
- if (m_use_controller->isChecked())
- spin->OnControllerValueChanged(controller_value);
- return (spin->GetValue() - zero) / scale;
- }
- void TASInputWindow::changeEvent(QEvent* const event)
- {
- if (event->type() == QEvent::ActivationChange)
- {
- const bool active_window_is_tas_input =
- qobject_cast<TASInputWindow*>(QApplication::activeWindow()) != nullptr;
- // Switching between TAS Input windows will call SetTASInputFocus(true) twice, but that's fine.
- Host::GetInstance()->SetTASInputFocus(active_window_is_tas_input);
- }
- QDialog::changeEvent(event);
- }
|