mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-29 17:15:40 +00:00
Qt: Properly handle modifier keys for input
This commit is contained in:
parent
6d5eca13a6
commit
87889a13e0
|
@ -15,36 +15,52 @@ InputButtonBindingWidget::InputButtonBindingWidget(QtHostInterface* host_interfa
|
||||||
connect(this, &QPushButton::pressed, this, &InputButtonBindingWidget::onPressed);
|
connect(this, &QPushButton::pressed, this, &InputButtonBindingWidget::onPressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
InputButtonBindingWidget::~InputButtonBindingWidget() = default;
|
InputButtonBindingWidget::~InputButtonBindingWidget()
|
||||||
|
|
||||||
void InputButtonBindingWidget::keyPressEvent(QKeyEvent* event)
|
|
||||||
{
|
{
|
||||||
// ignore the key press if we're listening for input
|
|
||||||
if (isListeningForInput())
|
if (isListeningForInput())
|
||||||
return;
|
|
||||||
|
|
||||||
QPushButton::keyPressEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputButtonBindingWidget::keyReleaseEvent(QKeyEvent* event)
|
|
||||||
{
|
|
||||||
if (!isListeningForInput())
|
|
||||||
{
|
|
||||||
QPushButton::keyReleaseEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString key_name = QtUtils::GetKeyIdentifier(event->key());
|
|
||||||
if (!key_name.isEmpty())
|
|
||||||
{
|
|
||||||
// TODO: Update input map
|
|
||||||
m_current_binding_value = QStringLiteral("Keyboard/%1").arg(key_name);
|
|
||||||
m_host_interface->getQSettings().setValue(m_setting_name, m_current_binding_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
stopListeningForInput();
|
stopListeningForInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InputButtonBindingWidget::eventFilter(QObject* watched, QEvent* event)
|
||||||
|
{
|
||||||
|
const QEvent::Type event_type = event->type();
|
||||||
|
|
||||||
|
// if the key is being released, set the input
|
||||||
|
if (event_type == QEvent::KeyRelease)
|
||||||
|
{
|
||||||
|
setNewBinding();
|
||||||
|
stopListeningForInput();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (event_type == QEvent::KeyPress)
|
||||||
|
{
|
||||||
|
QString binding = QtUtils::KeyEventToString(static_cast<QKeyEvent*>(event));
|
||||||
|
if (!binding.isEmpty())
|
||||||
|
m_new_binding_value = QStringLiteral("Keyboard/%1").arg(binding);
|
||||||
|
|
||||||
|
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->getQSettings().setValue(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()
|
void InputButtonBindingWidget::onPressed()
|
||||||
{
|
{
|
||||||
if (isListeningForInput())
|
if (isListeningForInput())
|
||||||
|
@ -75,6 +91,10 @@ void InputButtonBindingWidget::startListeningForInput()
|
||||||
&InputButtonBindingWidget::onInputListenTimerTimeout);
|
&InputButtonBindingWidget::onInputListenTimerTimeout);
|
||||||
m_input_listen_remaining_seconds = 5;
|
m_input_listen_remaining_seconds = 5;
|
||||||
setText(tr("Push Button... [%1]").arg(m_input_listen_remaining_seconds));
|
setText(tr("Push Button... [%1]").arg(m_input_listen_remaining_seconds));
|
||||||
|
|
||||||
|
installEventFilter(this);
|
||||||
|
grabKeyboard();
|
||||||
|
grabMouse();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputButtonBindingWidget::stopListeningForInput()
|
void InputButtonBindingWidget::stopListeningForInput()
|
||||||
|
@ -82,4 +102,8 @@ void InputButtonBindingWidget::stopListeningForInput()
|
||||||
setText(m_current_binding_value);
|
setText(m_current_binding_value);
|
||||||
delete m_input_listen_timer;
|
delete m_input_listen_timer;
|
||||||
m_input_listen_timer = nullptr;
|
m_input_listen_timer = nullptr;
|
||||||
|
|
||||||
|
releaseMouse();
|
||||||
|
releaseKeyboard();
|
||||||
|
removeEventFilter(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,7 @@ public:
|
||||||
~InputButtonBindingWidget();
|
~InputButtonBindingWidget();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent* event) override;
|
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||||
void keyReleaseEvent(QKeyEvent* event) override;
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void onPressed();
|
void onPressed();
|
||||||
|
@ -26,10 +25,12 @@ private:
|
||||||
bool isListeningForInput() const { return m_input_listen_timer != nullptr; }
|
bool isListeningForInput() const { return m_input_listen_timer != nullptr; }
|
||||||
void startListeningForInput();
|
void startListeningForInput();
|
||||||
void stopListeningForInput();
|
void stopListeningForInput();
|
||||||
|
void setNewBinding();
|
||||||
|
|
||||||
QtHostInterface* m_host_interface;
|
QtHostInterface* m_host_interface;
|
||||||
QString m_setting_name;
|
QString m_setting_name;
|
||||||
QString m_current_binding_value;
|
QString m_current_binding_value;
|
||||||
|
QString m_new_binding_value;
|
||||||
QTimer* m_input_listen_timer = nullptr;
|
QTimer* m_input_listen_timer = nullptr;
|
||||||
u32 m_input_listen_remaining_seconds = 0;
|
u32 m_input_listen_remaining_seconds = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "qtdisplaywindow.h"
|
#include "qtdisplaywindow.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#include "qtutils.h"
|
||||||
#include "qthostinterface.h"
|
#include "qthostinterface.h"
|
||||||
#include <QtGui/QKeyEvent>
|
#include <QtGui/QKeyEvent>
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@ void QtDisplayWindow::keyPressEvent(QKeyEvent* event)
|
||||||
if (event->isAutoRepeat())
|
if (event->isAutoRepeat())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_host_interface->handleKeyEvent(event->key(), true);
|
m_host_interface->handleKeyEvent(QtUtils::KeyEventToInt(event), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtDisplayWindow::keyReleaseEvent(QKeyEvent* event)
|
void QtDisplayWindow::keyReleaseEvent(QKeyEvent* event)
|
||||||
|
@ -83,7 +84,7 @@ void QtDisplayWindow::keyReleaseEvent(QKeyEvent* event)
|
||||||
if (event->isAutoRepeat())
|
if (event->isAutoRepeat())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_host_interface->handleKeyEvent(event->key(), false);
|
m_host_interface->handleKeyEvent(QtUtils::KeyEventToInt(event), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtDisplayWindow::resizeEvent(QResizeEvent* event)
|
void QtDisplayWindow::resizeEvent(QResizeEvent* event)
|
||||||
|
|
|
@ -297,7 +297,7 @@ void QtHostInterface::addButtonToInputMap(const QString& binding, InputButtonHan
|
||||||
const QString button = binding.section('/', 1, 1);
|
const QString button = binding.section('/', 1, 1);
|
||||||
if (device == QStringLiteral("Keyboard"))
|
if (device == QStringLiteral("Keyboard"))
|
||||||
{
|
{
|
||||||
std::optional<int> key_id = QtUtils::GetKeyIdForIdentifier(button);
|
std::optional<int> key_id = QtUtils::ParseKeyString(button);
|
||||||
if (!key_id.has_value())
|
if (!key_id.has_value())
|
||||||
{
|
{
|
||||||
qWarning() << "Unknown keyboard key " << button;
|
qWarning() << "Unknown keyboard key " << button;
|
||||||
|
|
|
@ -1,10 +1,32 @@
|
||||||
#include "qtutils.h"
|
#include "qtutils.h"
|
||||||
|
#include <QtGui/QKeyEvent>
|
||||||
|
#include <QtWidgets/QDialog>
|
||||||
|
#include <QtWidgets/QMainWindow>
|
||||||
#include <QtWidgets/QTableView>
|
#include <QtWidgets/QTableView>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
namespace QtUtils {
|
namespace QtUtils {
|
||||||
|
|
||||||
|
QWidget* GetRootWidget(QWidget* widget, bool stop_at_window_or_dialog)
|
||||||
|
{
|
||||||
|
QWidget* next_parent = widget->parentWidget();
|
||||||
|
while (next_parent)
|
||||||
|
{
|
||||||
|
if (stop_at_window_or_dialog && (widget->metaObject()->inherits(&QMainWindow::staticMetaObject) ||
|
||||||
|
widget->metaObject()->inherits(&QDialog::staticMetaObject)))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
widget = next_parent;
|
||||||
|
next_parent = widget->parentWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
void ResizeColumnsForTableView(QTableView* view, const std::initializer_list<int>& widths)
|
void ResizeColumnsForTableView(QTableView* view, const std::initializer_list<int>& widths)
|
||||||
{
|
{
|
||||||
const int total_width =
|
const int total_width =
|
||||||
|
@ -461,6 +483,22 @@ static const std::map<int, QString> s_qt_key_names = {
|
||||||
{Qt::Key_Camera, QStringLiteral("Camera")},
|
{Qt::Key_Camera, QStringLiteral("Camera")},
|
||||||
{Qt::Key_CameraFocus, QStringLiteral("CameraFocus")}};
|
{Qt::Key_CameraFocus, QStringLiteral("CameraFocus")}};
|
||||||
|
|
||||||
|
struct QtKeyModifierEntry
|
||||||
|
{
|
||||||
|
Qt::KeyboardModifier mod;
|
||||||
|
Qt::Key key;
|
||||||
|
QString name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::array<QtKeyModifierEntry, 5> s_qt_key_modifiers = {
|
||||||
|
{{Qt::ShiftModifier, Qt::Key_Shift, QStringLiteral("Shift")},
|
||||||
|
{Qt::ControlModifier, Qt::Key_Control, QStringLiteral("Control")},
|
||||||
|
{Qt::AltModifier, Qt::Key_Alt, QStringLiteral("Alt")},
|
||||||
|
{Qt::MetaModifier, Qt::Key_Meta, QStringLiteral("Meta")},
|
||||||
|
{Qt::KeypadModifier, static_cast<Qt::Key>(0), QStringLiteral("Keypad")}}};
|
||||||
|
static const Qt::KeyboardModifiers s_qt_modifier_mask =
|
||||||
|
Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier;
|
||||||
|
|
||||||
QString GetKeyIdentifier(int key)
|
QString GetKeyIdentifier(int key)
|
||||||
{
|
{
|
||||||
const auto it = s_qt_key_names.find(key);
|
const auto it = s_qt_key_names.find(key);
|
||||||
|
@ -478,4 +516,59 @@ std::optional<int> GetKeyIdForIdentifier(const QString& key_identifier)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString KeyEventToString(const QKeyEvent* ke)
|
||||||
|
{
|
||||||
|
const int key = ke->key();
|
||||||
|
QString key_name = GetKeyIdentifier(key);
|
||||||
|
if (key_name.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
QString ret;
|
||||||
|
const Qt::KeyboardModifiers mods = ke->modifiers();
|
||||||
|
for (const QtKeyModifierEntry& mod : s_qt_key_modifiers)
|
||||||
|
{
|
||||||
|
if (mods & mod.mod && key != mod.key)
|
||||||
|
{
|
||||||
|
ret.append(mod.name);
|
||||||
|
ret.append('+');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.append(key_name);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> ParseKeyString(const QString& key_str)
|
||||||
|
{
|
||||||
|
const QStringList sections = key_str.split('+');
|
||||||
|
std::optional<int> key_id = GetKeyIdForIdentifier(sections.last());
|
||||||
|
if (!key_id)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
int ret = key_id.value();
|
||||||
|
|
||||||
|
if (sections.size() > 1)
|
||||||
|
{
|
||||||
|
const int num_modifiers = sections.size() - 1;
|
||||||
|
for (int i = 0; i < num_modifiers; i++)
|
||||||
|
{
|
||||||
|
for (const QtKeyModifierEntry& mod : s_qt_key_modifiers)
|
||||||
|
{
|
||||||
|
if (sections[i] == mod.name)
|
||||||
|
{
|
||||||
|
ret |= static_cast<int>(mod.mod);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KeyEventToInt(const QKeyEvent* ke)
|
||||||
|
{
|
||||||
|
return static_cast<int>(ke->modifiers() & s_qt_modifier_mask) | ke->key();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QtUtils
|
} // namespace QtUtils
|
|
@ -3,10 +3,15 @@
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
class QKeyEvent;
|
||||||
class QTableView;
|
class QTableView;
|
||||||
|
class QWidget;
|
||||||
|
|
||||||
namespace QtUtils {
|
namespace QtUtils {
|
||||||
|
|
||||||
|
/// Returns the greatest parent of a widget, i.e. its dialog/window.
|
||||||
|
QWidget* GetRootWidget(QWidget* widget, bool stop_at_window_or_dialog = true);
|
||||||
|
|
||||||
/// Resizes columns of the table view to at the specified widths. A width of -1 will stretch the column to use the
|
/// Resizes columns of the table view to at the specified widths. A width of -1 will stretch the column to use the
|
||||||
/// remaining space.
|
/// remaining space.
|
||||||
void ResizeColumnsForTableView(QTableView* view, const std::initializer_list<int>& widths);
|
void ResizeColumnsForTableView(QTableView* view, const std::initializer_list<int>& widths);
|
||||||
|
@ -17,4 +22,13 @@ QString GetKeyIdentifier(int key);
|
||||||
/// Returns the integer Qt key ID for an identifier.
|
/// Returns the integer Qt key ID for an identifier.
|
||||||
std::optional<int> GetKeyIdForIdentifier(const QString& key_identifier);
|
std::optional<int> GetKeyIdForIdentifier(const QString& key_identifier);
|
||||||
|
|
||||||
|
/// Stringizes a key event.
|
||||||
|
QString KeyEventToString(const QKeyEvent* ke);
|
||||||
|
|
||||||
|
/// Returns an integer id for a stringized key event. Modifiers are in the upper bits.
|
||||||
|
std::optional<int> ParseKeyString(const QString& key_str);
|
||||||
|
|
||||||
|
/// Returns a key id for a key event, including any modifiers.
|
||||||
|
int KeyEventToInt(const QKeyEvent* ke);
|
||||||
|
|
||||||
} // namespace QtUtils
|
} // namespace QtUtils
|
Loading…
Reference in a new issue