diff --git a/src/duckstation-qt/controllerbindingwidgets.cpp b/src/duckstation-qt/controllerbindingwidgets.cpp index 03a0e0d824..4d60842d78 100644 --- a/src/duckstation-qt/controllerbindingwidgets.cpp +++ b/src/duckstation-qt/controllerbindingwidgets.cpp @@ -388,7 +388,7 @@ void ControllerBindingWidget::createBindingWidgets(QWidget* parent) scrollarea->setWidget(scrollarea_widget); scrollarea->setWidgetResizable(true); scrollarea->setFrameShape(QFrame::StyledPanel); - scrollarea->setFrameShadow(QFrame::Plain); + scrollarea->setFrameShadow(QFrame::Sunken); // We do axes and buttons separately, so we can figure out how many columns to use. constexpr int NUM_AXIS_COLUMNS = 2; diff --git a/src/duckstation-qt/inputbindingdialog.cpp b/src/duckstation-qt/inputbindingdialog.cpp index dfcdc0fbc1..b74bf3573d 100644 --- a/src/duckstation-qt/inputbindingdialog.cpp +++ b/src/duckstation-qt/inputbindingdialog.cpp @@ -1,11 +1,14 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "inputbindingdialog.h" -#include "common/bitutils.h" +#include "controllersettingwidgetbinder.h" #include "inputbindingwidgets.h" #include "qthost.h" #include "qtutils.h" + +#include "common/bitutils.h" + #include #include #include @@ -27,6 +30,28 @@ InputBindingDialog::InputBindingDialog(SettingsInterface* sif, InputBindingInfo: connect(m_ui.clearBindings, &QPushButton::clicked, this, &InputBindingDialog::onClearBindingsButtonClicked); connect(m_ui.buttonBox, &QDialogButtonBox::rejected, [this]() { done(0); }); updateList(); + + // Only show the sensitivity controls for binds where it's applicable. + if (bind_type == InputBindingInfo::Type::Button || bind_type == InputBindingInfo::Type::Axis || + bind_type == InputBindingInfo::Type::HalfAxis) + { + ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized(sif, m_ui.sensitivity, m_section_name, + fmt::format("{}Scale", m_key_name), 100.0f, 1.0f); + ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized( + sif, m_ui.deadzone, m_section_name, fmt::format("{}Deadzone", m_key_name), 100.0f, 0.0f); + + connect(m_ui.sensitivity, &QSlider::valueChanged, this, &InputBindingDialog::onSensitivityChanged); + connect(m_ui.resetSensitivity, &QToolButton::clicked, this, &InputBindingDialog::onResetSensitivityClicked); + connect(m_ui.deadzone, &QSlider::valueChanged, this, &InputBindingDialog::onDeadzoneChanged); + connect(m_ui.resetDeadzone, &QToolButton::clicked, this, &InputBindingDialog::onResetDeadzoneClicked); + + onSensitivityChanged(m_ui.sensitivity->value()); + onDeadzoneChanged(m_ui.deadzone->value()); + } + else + { + m_ui.verticalLayout->removeWidget(m_ui.sensitivityWidget); + } } InputBindingDialog::~InputBindingDialog() @@ -302,6 +327,56 @@ void InputBindingDialog::inputManagerHookCallback(InputBindingKey key, float val } } +void InputBindingDialog::onSensitivityChanged(int value) +{ + m_ui.sensitivityValue->setText(tr("%1%").arg(value)); +} + +void InputBindingDialog::onResetDeadzoneClicked() +{ + m_ui.deadzone->setValue(0); + + // May as well remove from the config completely, since it's the default. + const TinyString key = TinyString::from_format("{}Deadzone", m_key_name); + if (m_sif) + { + m_sif->DeleteValue(m_section_name.c_str(), key); + QtHost::SaveGameSettings(m_sif, false); + g_emu_thread->reloadGameSettings(false); + } + else + { + Host::DeleteBaseSettingValue(m_section_name.c_str(), key); + Host::CommitBaseSettingChanges(); + g_emu_thread->applySettings(false); + } +} + +void InputBindingDialog::onDeadzoneChanged(int value) +{ + m_ui.deadzoneValue->setText(tr("%1%").arg(value)); +} + +void InputBindingDialog::onResetSensitivityClicked() +{ + m_ui.sensitivity->setValue(100); + + // May as well remove from the config completely, since it's the default. + const TinyString key = TinyString::from_format("{}Scale", m_key_name); + if (m_sif) + { + m_sif->DeleteValue(m_section_name.c_str(), key); + QtHost::SaveGameSettings(m_sif, false); + g_emu_thread->reloadGameSettings(false); + } + else + { + Host::DeleteBaseSettingValue(m_section_name.c_str(), key); + Host::CommitBaseSettingChanges(); + g_emu_thread->applySettings(false); + } +} + void InputBindingDialog::hookInputManager() { InputManager::SetHook([this](InputBindingKey key, float value) { diff --git a/src/duckstation-qt/inputbindingdialog.h b/src/duckstation-qt/inputbindingdialog.h index 242fca34b3..2796ebf6f4 100644 --- a/src/duckstation-qt/inputbindingdialog.h +++ b/src/duckstation-qt/inputbindingdialog.h @@ -1,10 +1,10 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once #include "common/types.h" -#include "util/input_manager.h" #include "ui_inputbindingdialog.h" +#include "util/input_manager.h" #include #include #include @@ -48,6 +48,11 @@ protected Q_SLOTS: void hookInputManager(); void unhookInputManager(); + void onSensitivityChanged(int value); + void onResetDeadzoneClicked(); + void onDeadzoneChanged(int value); + void onResetSensitivityClicked(); + Ui::InputBindingDialog m_ui; SettingsInterface* m_sif; diff --git a/src/duckstation-qt/inputbindingdialog.ui b/src/duckstation-qt/inputbindingdialog.ui index c951b5a377..b9bced95cc 100644 --- a/src/duckstation-qt/inputbindingdialog.ui +++ b/src/duckstation-qt/inputbindingdialog.ui @@ -3,14 +3,14 @@ InputBindingDialog - Qt::WindowModal + Qt::WindowModality::WindowModal 0 0 533 - 283 + 266 @@ -30,6 +30,113 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 200 + + + 100 + + + Qt::Orientation::Horizontal + + + QSlider::TickPosition::TicksBelow + + + + + + + 0 + + + 100 + + + 1 + + + Qt::Orientation::Horizontal + + + QSlider::TickPosition::TicksBelow + + + 5 + + + + + + + Sensitivity: + + + + + + + 100% + + + + + + + Deadzone: + + + + + + + 100% + + + + + + + Reset Volume + + + + + + + + + + Reset Fast Forward Volume + + + + + + + + + @@ -63,7 +170,7 @@ - QDialogButtonBox::Close + QDialogButtonBox::StandardButton::Close diff --git a/src/util/input_manager.cpp b/src/util/input_manager.cpp index 027d80444a..28fd7f61ba 100644 --- a/src/util/input_manager.cpp +++ b/src/util/input_manager.cpp @@ -109,6 +109,7 @@ static void PrettifyInputBindingPart(std::string_view binding, SmallString& ret, static void AddBindings(const std::vector& bindings, const InputEventHandler& handler); static bool IsAxisHandler(const InputEventHandler& handler); +static float ApplySingleBindingScale(float sensitivity, float deadzone, float value); static void AddHotkeyBindings(SettingsInterface& si); static void AddPadBindings(SettingsInterface& si, const std::string& section, u32 pad, @@ -777,6 +778,12 @@ std::optional InputManager::ParseSensorKey(std::string_view sou // Binding Enumeration // ------------------------------------------------------------------------ +float InputManager::ApplySingleBindingScale(float scale, float deadzone, float value) +{ + const float svalue = std::clamp(value * scale, 0.0f, 1.0f); + return (deadzone > 0.0f && svalue < deadzone) ? 0.0f : svalue; +} + std::vector InputManager::GetHotkeyList() { std::vector ret; @@ -818,13 +825,18 @@ void InputManager::AddPadBindings(SettingsInterface& si, const std::string& sect { if (!bindings.empty()) { - AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index](float value) { + const float sensitivity = + si.GetFloatValue(section.c_str(), TinyString::from_format("{}Scale", bi.name), 1.0f); + const float deadzone = + si.GetFloatValue(section.c_str(), TinyString::from_format("{}Deadzone", bi.name), 0.0f); + AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index, sensitivity, + deadzone](float value) { if (!System::IsValid()) return; Controller* c = System::GetController(pad_index); if (c) - c->SetBindState(bind_index, value); + c->SetBindState(bind_index, ApplySingleBindingScale(sensitivity, deadzone, value)); }}); } }