Qt: Support axes in input binding widgets
This commit is contained in:
parent
149cbf6457
commit
b7dfe06f74
|
@ -6,20 +6,102 @@
|
|||
#include <QtCore/QTimer>
|
||||
#include <QtGui/QKeyEvent>
|
||||
|
||||
InputButtonBindingWidget::InputButtonBindingWidget(QtHostInterface* host_interface, QString setting_name,
|
||||
QWidget* parent)
|
||||
InputBindingWidget::InputBindingWidget(QtHostInterface* host_interface, QString setting_name, QWidget* parent)
|
||||
: QPushButton(parent), m_host_interface(host_interface), m_setting_name(std::move(setting_name))
|
||||
{
|
||||
m_current_binding_value = m_host_interface->getSettingValue(m_setting_name).toString();
|
||||
setText(m_current_binding_value);
|
||||
|
||||
connect(this, &QPushButton::pressed, this, &InputButtonBindingWidget::onPressed);
|
||||
connect(this, &QPushButton::pressed, this, &InputBindingWidget::onPressed);
|
||||
}
|
||||
|
||||
InputBindingWidget::~InputBindingWidget()
|
||||
{
|
||||
Q_ASSERT(!isListeningForInput());
|
||||
}
|
||||
|
||||
bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
const QEvent::Type event_type = event->type();
|
||||
|
||||
if (event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonRelease ||
|
||||
event_type == QEvent::MouseButtonDblClick)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InputBindingWidget::setNewBinding()
|
||||
{
|
||||
if (m_new_binding_value.isEmpty())
|
||||
return;
|
||||
|
||||
m_host_interface->putSettingValue(m_setting_name, m_new_binding_value);
|
||||
m_host_interface->updateInputMap();
|
||||
|
||||
m_current_binding_value = std::move(m_new_binding_value);
|
||||
m_new_binding_value.clear();
|
||||
}
|
||||
|
||||
void InputBindingWidget::onPressed()
|
||||
{
|
||||
if (isListeningForInput())
|
||||
stopListeningForInput();
|
||||
|
||||
startListeningForInput();
|
||||
}
|
||||
|
||||
void InputBindingWidget::onInputListenTimerTimeout()
|
||||
{
|
||||
m_input_listen_remaining_seconds--;
|
||||
if (m_input_listen_remaining_seconds == 0)
|
||||
{
|
||||
stopListeningForInput();
|
||||
return;
|
||||
}
|
||||
|
||||
setText(tr("Push Button/Axis... [%1]").arg(m_input_listen_remaining_seconds));
|
||||
}
|
||||
|
||||
void InputBindingWidget::startListeningForInput()
|
||||
{
|
||||
m_input_listen_timer = new QTimer(this);
|
||||
m_input_listen_timer->setSingleShot(false);
|
||||
m_input_listen_timer->start(1000);
|
||||
|
||||
m_input_listen_timer->connect(m_input_listen_timer, &QTimer::timeout, this,
|
||||
&InputBindingWidget::onInputListenTimerTimeout);
|
||||
m_input_listen_remaining_seconds = 5;
|
||||
setText(tr("Push Button/Axis... [%1]").arg(m_input_listen_remaining_seconds));
|
||||
|
||||
installEventFilter(this);
|
||||
grabKeyboard();
|
||||
grabMouse();
|
||||
}
|
||||
|
||||
void InputBindingWidget::stopListeningForInput()
|
||||
{
|
||||
setText(m_current_binding_value);
|
||||
delete m_input_listen_timer;
|
||||
m_input_listen_timer = nullptr;
|
||||
|
||||
releaseMouse();
|
||||
releaseKeyboard();
|
||||
removeEventFilter(this);
|
||||
}
|
||||
|
||||
InputButtonBindingWidget::InputButtonBindingWidget(QtHostInterface* host_interface, QString setting_name,
|
||||
QWidget* parent)
|
||||
: InputBindingWidget(host_interface, setting_name, parent)
|
||||
{
|
||||
}
|
||||
|
||||
InputButtonBindingWidget::~InputButtonBindingWidget()
|
||||
{
|
||||
if (isListeningForInput())
|
||||
stopListeningForInput();
|
||||
InputButtonBindingWidget::stopListeningForInput();
|
||||
}
|
||||
|
||||
bool InputButtonBindingWidget::eventFilter(QObject* watched, QEvent* event)
|
||||
|
@ -41,45 +123,8 @@ bool InputButtonBindingWidget::eventFilter(QObject* watched, QEvent* event)
|
|||
|
||||
return true;
|
||||
}
|
||||
else if (event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonRelease ||
|
||||
event_type == QEvent::MouseButtonDblClick)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InputButtonBindingWidget::setNewBinding()
|
||||
{
|
||||
if (m_new_binding_value.isEmpty())
|
||||
return;
|
||||
|
||||
m_host_interface->putSettingValue(m_setting_name, m_new_binding_value);
|
||||
m_host_interface->updateInputMap();
|
||||
|
||||
m_current_binding_value = std::move(m_new_binding_value);
|
||||
m_new_binding_value.clear();
|
||||
}
|
||||
|
||||
void InputButtonBindingWidget::onPressed()
|
||||
{
|
||||
if (isListeningForInput())
|
||||
stopListeningForInput();
|
||||
|
||||
startListeningForInput();
|
||||
}
|
||||
|
||||
void InputButtonBindingWidget::onInputListenTimerTimeout()
|
||||
{
|
||||
m_input_listen_remaining_seconds--;
|
||||
if (m_input_listen_remaining_seconds == 0)
|
||||
{
|
||||
stopListeningForInput();
|
||||
return;
|
||||
}
|
||||
|
||||
setText(tr("Push Button... [%1]").arg(m_input_listen_remaining_seconds));
|
||||
return InputBindingWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void InputButtonBindingWidget::hookControllerInput()
|
||||
|
@ -88,6 +133,10 @@ void InputButtonBindingWidget::hookControllerInput()
|
|||
g_sdl_controller_interface.SetHook([this](const SDLControllerInterface::Hook& ei) {
|
||||
if (ei.type == SDLControllerInterface::Hook::Type::Axis)
|
||||
{
|
||||
// wait until it's at least half pushed so we don't get confused between axises with small movement
|
||||
if (std::abs(ei.value) < 0.5f)
|
||||
return SDLControllerInterface::Hook::CallbackResult::ContinueMonitoring;
|
||||
|
||||
// TODO: this probably should consider the "last value"
|
||||
QMetaObject::invokeMethod(this, "bindToControllerAxis", Q_ARG(int, ei.controller_index),
|
||||
Q_ARG(int, ei.button_or_axis_number), Q_ARG(bool, ei.value > 0));
|
||||
|
@ -127,29 +176,67 @@ void InputButtonBindingWidget::bindToControllerButton(int controller_index, int
|
|||
|
||||
void InputButtonBindingWidget::startListeningForInput()
|
||||
{
|
||||
m_input_listen_timer = new QTimer(this);
|
||||
m_input_listen_timer->setSingleShot(false);
|
||||
m_input_listen_timer->start(1000);
|
||||
|
||||
m_input_listen_timer->connect(m_input_listen_timer, &QTimer::timeout, this,
|
||||
&InputButtonBindingWidget::onInputListenTimerTimeout);
|
||||
m_input_listen_remaining_seconds = 5;
|
||||
setText(tr("Push Button... [%1]").arg(m_input_listen_remaining_seconds));
|
||||
|
||||
installEventFilter(this);
|
||||
grabKeyboard();
|
||||
grabMouse();
|
||||
InputBindingWidget::startListeningForInput();
|
||||
hookControllerInput();
|
||||
}
|
||||
|
||||
void InputButtonBindingWidget::stopListeningForInput()
|
||||
{
|
||||
setText(m_current_binding_value);
|
||||
delete m_input_listen_timer;
|
||||
m_input_listen_timer = nullptr;
|
||||
|
||||
unhookControllerInput();
|
||||
releaseMouse();
|
||||
releaseKeyboard();
|
||||
removeEventFilter(this);
|
||||
InputBindingWidget::stopListeningForInput();
|
||||
}
|
||||
|
||||
InputAxisBindingWidget::InputAxisBindingWidget(QtHostInterface* host_interface, QString setting_name, QWidget* parent)
|
||||
: InputBindingWidget(host_interface, setting_name, parent)
|
||||
{
|
||||
}
|
||||
|
||||
InputAxisBindingWidget::~InputAxisBindingWidget()
|
||||
{
|
||||
if (isListeningForInput())
|
||||
InputAxisBindingWidget::stopListeningForInput();
|
||||
}
|
||||
|
||||
void InputAxisBindingWidget::hookControllerInput()
|
||||
{
|
||||
m_host_interface->enableBackgroundControllerPolling();
|
||||
g_sdl_controller_interface.SetHook([this](const SDLControllerInterface::Hook& ei) {
|
||||
if (ei.type == SDLControllerInterface::Hook::Type::Axis)
|
||||
{
|
||||
// wait until it's at least half pushed so we don't get confused between axises with small movement
|
||||
if (std::abs(ei.value) < 0.5f)
|
||||
return SDLControllerInterface::Hook::CallbackResult::ContinueMonitoring;
|
||||
|
||||
QMetaObject::invokeMethod(this, "bindToControllerAxis", Q_ARG(int, ei.controller_index),
|
||||
Q_ARG(int, ei.button_or_axis_number));
|
||||
return SDLControllerInterface::Hook::CallbackResult::StopMonitoring;
|
||||
}
|
||||
|
||||
return SDLControllerInterface::Hook::CallbackResult::ContinueMonitoring;
|
||||
});
|
||||
}
|
||||
|
||||
void InputAxisBindingWidget::unhookControllerInput()
|
||||
{
|
||||
g_sdl_controller_interface.ClearHook();
|
||||
m_host_interface->disableBackgroundControllerPolling();
|
||||
}
|
||||
|
||||
void InputAxisBindingWidget::bindToControllerAxis(int controller_index, int axis_index)
|
||||
{
|
||||
m_new_binding_value = QStringLiteral("Controller%1/Axis%2").arg(controller_index).arg(axis_index);
|
||||
setNewBinding();
|
||||
stopListeningForInput();
|
||||
}
|
||||
|
||||
void InputAxisBindingWidget::startListeningForInput()
|
||||
{
|
||||
InputBindingWidget::startListeningForInput();
|
||||
hookControllerInput();
|
||||
}
|
||||
|
||||
void InputAxisBindingWidget::stopListeningForInput()
|
||||
{
|
||||
unhookControllerInput();
|
||||
InputBindingWidget::stopListeningForInput();
|
||||
}
|
||||
|
|
|
@ -6,7 +6,37 @@ class QTimer;
|
|||
|
||||
class QtHostInterface;
|
||||
|
||||
class InputButtonBindingWidget : public QPushButton
|
||||
class InputBindingWidget : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
InputBindingWidget(QtHostInterface* host_interface, QString setting_name, QWidget* parent);
|
||||
~InputBindingWidget();
|
||||
|
||||
protected:
|
||||
virtual bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
|
||||
protected Q_SLOTS:
|
||||
void onPressed();
|
||||
void onInputListenTimerTimeout();
|
||||
|
||||
protected:
|
||||
virtual void startListeningForInput();
|
||||
virtual void stopListeningForInput();
|
||||
|
||||
bool isListeningForInput() const { return m_input_listen_timer != nullptr; }
|
||||
void setNewBinding();
|
||||
|
||||
QtHostInterface* m_host_interface;
|
||||
QString m_setting_name;
|
||||
QString m_current_binding_value;
|
||||
QString m_new_binding_value;
|
||||
QTimer* m_input_listen_timer = nullptr;
|
||||
u32 m_input_listen_remaining_seconds = 0;
|
||||
};
|
||||
|
||||
class InputButtonBindingWidget : public InputBindingWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -18,23 +48,30 @@ protected:
|
|||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void onPressed();
|
||||
void onInputListenTimerTimeout();
|
||||
void bindToControllerAxis(int controller_index, int axis_index, bool positive);
|
||||
void bindToControllerButton(int controller_index, int button_index);
|
||||
|
||||
private:
|
||||
bool isListeningForInput() const { return m_input_listen_timer != nullptr; }
|
||||
void startListeningForInput();
|
||||
void stopListeningForInput();
|
||||
void setNewBinding();
|
||||
protected:
|
||||
void startListeningForInput() override;
|
||||
void stopListeningForInput() override;
|
||||
void hookControllerInput();
|
||||
void unhookControllerInput();
|
||||
};
|
||||
|
||||
class InputAxisBindingWidget : public InputBindingWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
InputAxisBindingWidget(QtHostInterface* host_interface, QString setting_name, QWidget* parent);
|
||||
~InputAxisBindingWidget();
|
||||
|
||||
private Q_SLOTS:
|
||||
void bindToControllerAxis(int controller_index, int axis_index);
|
||||
|
||||
protected:
|
||||
void startListeningForInput() override;
|
||||
void stopListeningForInput() override;
|
||||
void hookControllerInput();
|
||||
void unhookControllerInput();
|
||||
|
||||
QtHostInterface* m_host_interface;
|
||||
QString m_setting_name;
|
||||
QString m_current_binding_value;
|
||||
QString m_new_binding_value;
|
||||
QTimer* m_input_listen_timer = nullptr;
|
||||
u32 m_input_listen_remaining_seconds = 0;
|
||||
};
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
#include "core/host_interface.h"
|
||||
#include "core/system.h"
|
||||
#include "sdl_initializer.h"
|
||||
#include <cmath>
|
||||
#include <SDL.h>
|
||||
#include <cmath>
|
||||
Log_SetChannel(SDLControllerInterface);
|
||||
|
||||
SDLControllerInterface g_sdl_controller_interface;
|
||||
|
@ -330,16 +330,13 @@ void SDLControllerInterface::SetDefaultBindings()
|
|||
|
||||
bool SDLControllerInterface::HandleControllerAxisEvent(const SDL_Event* ev)
|
||||
{
|
||||
Log_DebugPrintf("controller %d axis %d %d", ev->caxis.which, ev->caxis.axis, ev->caxis.value);
|
||||
|
||||
// TODO: Make deadzone customizable.
|
||||
static constexpr float deadzone = 8192.0f / 32768.0f;
|
||||
|
||||
const float value = static_cast<float>(ev->caxis.value) / (ev->caxis.value < 0 ? 32768.0f : 32767.0f);
|
||||
const bool outside_deadzone = (std::abs(value) >= deadzone);
|
||||
Log_DebugPrintf("controller %d axis %d %d %f", ev->caxis.which, ev->caxis.axis, ev->caxis.value, value);
|
||||
|
||||
// only send monitor events if it's outside of the deadzone, otherwise it's really hard to bind
|
||||
if (outside_deadzone && DoEventHook(Hook::Type::Axis, ev->caxis.which, ev->caxis.axis, value))
|
||||
if (DoEventHook(Hook::Type::Axis, ev->caxis.which, ev->caxis.axis, value))
|
||||
return true;
|
||||
|
||||
auto it = m_controllers.find(ev->caxis.which);
|
||||
|
@ -355,6 +352,7 @@ bool SDLControllerInterface::HandleControllerAxisEvent(const SDL_Event* ev)
|
|||
}
|
||||
|
||||
// set the other direction to false so large movements don't leave the opposite on
|
||||
const bool outside_deadzone = (std::abs(value) >= deadzone);
|
||||
const bool positive = (value >= 0.0f);
|
||||
const ButtonCallback& other_button_cb = cd.axis_button_mapping[ev->caxis.axis][BoolToUInt8(!positive)];
|
||||
const ButtonCallback& button_cb = cd.axis_button_mapping[ev->caxis.axis][BoolToUInt8(positive)];
|
||||
|
|
Loading…
Reference in New Issue