Qt: Support axes in input binding widgets
This commit is contained in:
parent
149cbf6457
commit
b7dfe06f74
|
@ -6,20 +6,102 @@
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
#include <QtGui/QKeyEvent>
|
#include <QtGui/QKeyEvent>
|
||||||
|
|
||||||
InputButtonBindingWidget::InputButtonBindingWidget(QtHostInterface* host_interface, QString setting_name,
|
InputBindingWidget::InputBindingWidget(QtHostInterface* host_interface, QString setting_name, QWidget* parent)
|
||||||
QWidget* parent)
|
|
||||||
: QPushButton(parent), m_host_interface(host_interface), m_setting_name(std::move(setting_name))
|
: 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();
|
m_current_binding_value = m_host_interface->getSettingValue(m_setting_name).toString();
|
||||||
setText(m_current_binding_value);
|
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()
|
InputButtonBindingWidget::~InputButtonBindingWidget()
|
||||||
{
|
{
|
||||||
if (isListeningForInput())
|
if (isListeningForInput())
|
||||||
stopListeningForInput();
|
InputButtonBindingWidget::stopListeningForInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputButtonBindingWidget::eventFilter(QObject* watched, QEvent* event)
|
bool InputButtonBindingWidget::eventFilter(QObject* watched, QEvent* event)
|
||||||
|
@ -41,45 +123,8 @@ bool InputButtonBindingWidget::eventFilter(QObject* watched, QEvent* event)
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonRelease ||
|
|
||||||
event_type == QEvent::MouseButtonDblClick)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return InputBindingWidget::eventFilter(watched, event);
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputButtonBindingWidget::hookControllerInput()
|
void InputButtonBindingWidget::hookControllerInput()
|
||||||
|
@ -88,6 +133,10 @@ void InputButtonBindingWidget::hookControllerInput()
|
||||||
g_sdl_controller_interface.SetHook([this](const SDLControllerInterface::Hook& ei) {
|
g_sdl_controller_interface.SetHook([this](const SDLControllerInterface::Hook& ei) {
|
||||||
if (ei.type == SDLControllerInterface::Hook::Type::Axis)
|
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"
|
// TODO: this probably should consider the "last value"
|
||||||
QMetaObject::invokeMethod(this, "bindToControllerAxis", Q_ARG(int, ei.controller_index),
|
QMetaObject::invokeMethod(this, "bindToControllerAxis", Q_ARG(int, ei.controller_index),
|
||||||
Q_ARG(int, ei.button_or_axis_number), Q_ARG(bool, ei.value > 0));
|
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()
|
void InputButtonBindingWidget::startListeningForInput()
|
||||||
{
|
{
|
||||||
m_input_listen_timer = new QTimer(this);
|
InputBindingWidget::startListeningForInput();
|
||||||
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();
|
|
||||||
hookControllerInput();
|
hookControllerInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputButtonBindingWidget::stopListeningForInput()
|
void InputButtonBindingWidget::stopListeningForInput()
|
||||||
{
|
{
|
||||||
setText(m_current_binding_value);
|
|
||||||
delete m_input_listen_timer;
|
|
||||||
m_input_listen_timer = nullptr;
|
|
||||||
|
|
||||||
unhookControllerInput();
|
unhookControllerInput();
|
||||||
releaseMouse();
|
InputBindingWidget::stopListeningForInput();
|
||||||
releaseKeyboard();
|
}
|
||||||
removeEventFilter(this);
|
|
||||||
|
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 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
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -18,23 +48,30 @@ protected:
|
||||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void onPressed();
|
|
||||||
void onInputListenTimerTimeout();
|
|
||||||
void bindToControllerAxis(int controller_index, int axis_index, bool positive);
|
void bindToControllerAxis(int controller_index, int axis_index, bool positive);
|
||||||
void bindToControllerButton(int controller_index, int button_index);
|
void bindToControllerButton(int controller_index, int button_index);
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
bool isListeningForInput() const { return m_input_listen_timer != nullptr; }
|
void startListeningForInput() override;
|
||||||
void startListeningForInput();
|
void stopListeningForInput() override;
|
||||||
void stopListeningForInput();
|
void hookControllerInput();
|
||||||
void setNewBinding();
|
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 hookControllerInput();
|
||||||
void unhookControllerInput();
|
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/host_interface.h"
|
||||||
#include "core/system.h"
|
#include "core/system.h"
|
||||||
#include "sdl_initializer.h"
|
#include "sdl_initializer.h"
|
||||||
#include <cmath>
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
#include <cmath>
|
||||||
Log_SetChannel(SDLControllerInterface);
|
Log_SetChannel(SDLControllerInterface);
|
||||||
|
|
||||||
SDLControllerInterface g_sdl_controller_interface;
|
SDLControllerInterface g_sdl_controller_interface;
|
||||||
|
@ -330,16 +330,13 @@ void SDLControllerInterface::SetDefaultBindings()
|
||||||
|
|
||||||
bool SDLControllerInterface::HandleControllerAxisEvent(const SDL_Event* ev)
|
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.
|
// TODO: Make deadzone customizable.
|
||||||
static constexpr float deadzone = 8192.0f / 32768.0f;
|
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 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 (DoEventHook(Hook::Type::Axis, ev->caxis.which, ev->caxis.axis, value))
|
||||||
if (outside_deadzone && DoEventHook(Hook::Type::Axis, ev->caxis.which, ev->caxis.axis, value))
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
auto it = m_controllers.find(ev->caxis.which);
|
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
|
// 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 bool positive = (value >= 0.0f);
|
||||||
const ButtonCallback& other_button_cb = cd.axis_button_mapping[ev->caxis.axis][BoolToUInt8(!positive)];
|
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)];
|
const ButtonCallback& button_cb = cd.axis_button_mapping[ev->caxis.axis][BoolToUInt8(positive)];
|
||||||
|
|
Loading…
Reference in New Issue