mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-05-30 23:33:17 +00:00
Add UI to configure keyboard-to-controller mapping (#308)
* Add UI to configure keyboard-to-controller mapping * Add an optional "---fix" argument to format-checking script * clang fix --------- Co-authored-by: georgemoralis <giorgosmrls@gmail.com>
This commit is contained in:
parent
74c2888aaa
commit
fdb13a3b90
18 changed files with 1436 additions and 173 deletions
524
src/qt_gui/keyboardcontrolswindow.cpp
Normal file
524
src/qt_gui/keyboardcontrolswindow.cpp
Normal file
|
@ -0,0 +1,524 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "./ui_keyboardcontrolswindow.h"
|
||||
#include "common/config.h"
|
||||
#include "keyboardcontrolswindow.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace {
|
||||
static constexpr auto keyBindingsSettingsKey = "ShadPS4_Keyboard_Settings_KEY";
|
||||
static constexpr auto inputErrorTimerTimeout = 2000;
|
||||
} // namespace
|
||||
|
||||
void showError(const QString& message) {
|
||||
QMessageBox::critical(nullptr, "Error", message, QMessageBox::Ok);
|
||||
}
|
||||
|
||||
void showWarning(const QString& message) {
|
||||
QMessageBox::warning(nullptr, "Warning", message, QMessageBox::Ok);
|
||||
}
|
||||
|
||||
void showInfo(const QString& message) {
|
||||
QMessageBox::information(nullptr, "Info", message, QMessageBox::Ok);
|
||||
}
|
||||
|
||||
KeyboardControlsWindow::KeyboardControlsWindow(QWidget* parent)
|
||||
: QDialog(parent), ui(new Ui::KeyboardControlsWindow) {
|
||||
ui->setupUi(this);
|
||||
|
||||
m_keysMap = Config::getKeyboardBindingMap();
|
||||
|
||||
for (auto& pair : m_keysMap) {
|
||||
m_reverseKeysMap.emplace(pair.second, pair.first);
|
||||
}
|
||||
|
||||
m_listOfKeySequenceEdits = {ui->StartKeySequenceEdit, ui->SelectKeySequenceEdit,
|
||||
ui->LAnalogDownkeySequenceEdit, ui->LAnalogLeftkeySequenceEdit,
|
||||
ui->LAnalogUpkeySequenceEdit, ui->LAnalogRightkeySequenceEdit,
|
||||
ui->PSkeySequenceEdit, ui->RAnalogDownkeySequenceEdit,
|
||||
ui->RAnalogLeftkeySequenceEdit, ui->RAnalogUpkeySequenceEdit,
|
||||
ui->RAnalogRightkeySequenceEdit, ui->DPadLeftkeySequenceEdit,
|
||||
ui->DPadRightkeySequenceEdit, ui->DPadUpkeySequenceEdit,
|
||||
ui->DPadDownkeySequenceEdit, ui->L2keySequenceEdit,
|
||||
ui->L1keySequenceEdit, ui->CrossKeySequenceEdit,
|
||||
ui->R2KeySequenceEdit, ui->CircleKeySequenceEdit,
|
||||
ui->R1KeySequenceEdit, ui->SquareKeySequenceEdit,
|
||||
ui->TriangleKeySequenceEdit};
|
||||
|
||||
for (auto edit : m_listOfKeySequenceEdits) {
|
||||
edit->setStyleSheet("QLineEdit { qproperty-alignment: AlignCenter; }");
|
||||
QObject::connect(edit, &QKeySequenceEdit::editingFinished, this,
|
||||
&KeyboardControlsWindow::onEditingFinished);
|
||||
}
|
||||
|
||||
ui->StartKeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Start_Key]));
|
||||
ui->SelectKeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Select_Key]));
|
||||
ui->LAnalogDownkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::LAnalogDown_Key]));
|
||||
ui->LAnalogLeftkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::LAnalogLeft_Key]));
|
||||
ui->LAnalogUpkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::LAnalogUp_Key]));
|
||||
ui->LAnalogRightkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::LAnalogRight_Key]));
|
||||
ui->PSkeySequenceEdit->setKeySequence(convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::PS_Key]));
|
||||
ui->RAnalogDownkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::RAnalogDown_Key]));
|
||||
ui->RAnalogLeftkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::RAnalogLeft_Key]));
|
||||
ui->RAnalogUpkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::RAnalogUp_Key]));
|
||||
ui->RAnalogRightkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::RAnalogRight_Key]));
|
||||
ui->DPadLeftkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::DPadLeft_Key]));
|
||||
ui->DPadRightkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::DPadRight_Key]));
|
||||
ui->DPadUpkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::DPadUp_Key]));
|
||||
ui->DPadDownkeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::DPadDown_Key]));
|
||||
ui->L2keySequenceEdit->setKeySequence(convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::L2_Key]));
|
||||
ui->L1keySequenceEdit->setKeySequence(convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::L1_Key]));
|
||||
ui->CrossKeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Cross_Key]));
|
||||
ui->R2KeySequenceEdit->setKeySequence(convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::R2_Key]));
|
||||
ui->CircleKeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Circle_Key]));
|
||||
ui->R1KeySequenceEdit->setKeySequence(convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::R1_Key]));
|
||||
ui->SquareKeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Square_Key]));
|
||||
ui->TriangleKeySequenceEdit->setKeySequence(
|
||||
convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Triangle_Key]));
|
||||
|
||||
QObject::connect(ui->applyButton, &QPushButton::clicked,
|
||||
[this]() { validateAndSaveKeyBindings(); });
|
||||
|
||||
QObject::connect(ui->cancelButton, &QPushButton::clicked, [this]() { this->close(); });
|
||||
}
|
||||
|
||||
KeyboardControlsWindow::~KeyboardControlsWindow() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
const std::map<u32, KeysMapping>& KeyboardControlsWindow::getKeysMapping() const {
|
||||
return m_keysMap;
|
||||
}
|
||||
|
||||
void KeyboardControlsWindow::validateAndSaveKeyBindings() {
|
||||
int nOfUnconfiguredButtons = 0;
|
||||
for (auto& keyEdit : m_listOfKeySequenceEdits) {
|
||||
auto keySequence = keyEdit->keySequence();
|
||||
// If key sequence is empty (i.e. there is no key assigned to it) we highlight it in red
|
||||
if (keySequence.isEmpty()) {
|
||||
keyEdit->setStyleSheet("background-color: red; qproperty-alignment: AlignCenter;");
|
||||
QTimer::singleShot(inputErrorTimerTimeout, keyEdit, [keyEdit]() {
|
||||
keyEdit->setStyleSheet("qproperty-alignment: AlignCenter;"); // Reset to default
|
||||
});
|
||||
|
||||
++nOfUnconfiguredButtons;
|
||||
}
|
||||
}
|
||||
|
||||
if (nOfUnconfiguredButtons > 0) {
|
||||
showError("Some of the buttons were not configured");
|
||||
return;
|
||||
}
|
||||
|
||||
m_keysMap.clear();
|
||||
m_reverseKeysMap.clear();
|
||||
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->LAnalogDownkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::LAnalogDown_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->LAnalogLeftkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::LAnalogLeft_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->LAnalogUpkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::LAnalogUp_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->LAnalogRightkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::LAnalogRight_Key);
|
||||
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->PSkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::PS_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->StartKeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::Start_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->SelectKeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::Select_Key);
|
||||
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->RAnalogDownkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::RAnalogDown_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->RAnalogLeftkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::RAnalogLeft_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->RAnalogUpkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::RAnalogUp_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->RAnalogRightkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::RAnalogRight_Key);
|
||||
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->DPadLeftkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::DPadLeft_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->DPadRightkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::DPadRight_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->DPadUpkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::DPadUp_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->DPadDownkeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::DPadDown_Key);
|
||||
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->L1keySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::L1_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->L2keySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::L2_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->R1KeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::R1_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->R2KeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::R2_Key);
|
||||
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->CrossKeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::Cross_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->CircleKeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::Circle_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->SquareKeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::Square_Key);
|
||||
m_keysMap.emplace(convertQtKeyToSDL(ui->TriangleKeySequenceEdit->keySequence()[0].key()),
|
||||
KeysMapping::Triangle_Key);
|
||||
|
||||
for (auto& pair : m_keysMap) {
|
||||
m_reverseKeysMap.emplace(pair.second, pair.first);
|
||||
}
|
||||
|
||||
// Saving into settings (for permanent storage)
|
||||
Config::setKeyboardBindingMap(m_keysMap);
|
||||
|
||||
this->close();
|
||||
}
|
||||
|
||||
Qt::Key KeyboardControlsWindow::convertSDLKeyToQt(SDL_Keycode sdlKey) {
|
||||
switch (sdlKey) {
|
||||
case SDLK_A:
|
||||
return Qt::Key_A;
|
||||
case SDLK_B:
|
||||
return Qt::Key_B;
|
||||
case SDLK_C:
|
||||
return Qt::Key_C;
|
||||
case SDLK_D:
|
||||
return Qt::Key_D;
|
||||
case SDLK_E:
|
||||
return Qt::Key_E;
|
||||
case SDLK_F:
|
||||
return Qt::Key_F;
|
||||
case SDLK_G:
|
||||
return Qt::Key_G;
|
||||
case SDLK_H:
|
||||
return Qt::Key_H;
|
||||
case SDLK_I:
|
||||
return Qt::Key_I;
|
||||
case SDLK_J:
|
||||
return Qt::Key_J;
|
||||
case SDLK_K:
|
||||
return Qt::Key_K;
|
||||
case SDLK_L:
|
||||
return Qt::Key_L;
|
||||
case SDLK_M:
|
||||
return Qt::Key_M;
|
||||
case SDLK_N:
|
||||
return Qt::Key_N;
|
||||
case SDLK_O:
|
||||
return Qt::Key_O;
|
||||
case SDLK_P:
|
||||
return Qt::Key_P;
|
||||
case SDLK_Q:
|
||||
return Qt::Key_Q;
|
||||
case SDLK_R:
|
||||
return Qt::Key_R;
|
||||
case SDLK_S:
|
||||
return Qt::Key_S;
|
||||
case SDLK_T:
|
||||
return Qt::Key_T;
|
||||
case SDLK_U:
|
||||
return Qt::Key_U;
|
||||
case SDLK_V:
|
||||
return Qt::Key_V;
|
||||
case SDLK_W:
|
||||
return Qt::Key_W;
|
||||
case SDLK_X:
|
||||
return Qt::Key_X;
|
||||
case SDLK_Y:
|
||||
return Qt::Key_Y;
|
||||
case SDLK_Z:
|
||||
return Qt::Key_Z;
|
||||
case SDLK_0:
|
||||
return Qt::Key_0;
|
||||
case SDLK_1:
|
||||
return Qt::Key_1;
|
||||
case SDLK_2:
|
||||
return Qt::Key_2;
|
||||
case SDLK_3:
|
||||
return Qt::Key_3;
|
||||
case SDLK_4:
|
||||
return Qt::Key_4;
|
||||
case SDLK_5:
|
||||
return Qt::Key_5;
|
||||
case SDLK_6:
|
||||
return Qt::Key_6;
|
||||
case SDLK_7:
|
||||
return Qt::Key_7;
|
||||
case SDLK_8:
|
||||
return Qt::Key_8;
|
||||
case SDLK_9:
|
||||
return Qt::Key_9;
|
||||
case SDLK_SPACE:
|
||||
return Qt::Key_Space;
|
||||
case SDLK_RETURN:
|
||||
return Qt::Key_Return;
|
||||
case SDLK_ESCAPE:
|
||||
return Qt::Key_Escape;
|
||||
case SDLK_TAB:
|
||||
return Qt::Key_Tab;
|
||||
case SDLK_BACKSPACE:
|
||||
return Qt::Key_Backspace;
|
||||
case SDLK_DELETE:
|
||||
return Qt::Key_Delete;
|
||||
case SDLK_INSERT:
|
||||
return Qt::Key_Insert;
|
||||
case SDLK_HOME:
|
||||
return Qt::Key_Home;
|
||||
case SDLK_END:
|
||||
return Qt::Key_End;
|
||||
case SDLK_PAGEUP:
|
||||
return Qt::Key_PageUp;
|
||||
case SDLK_PAGEDOWN:
|
||||
return Qt::Key_PageDown;
|
||||
case SDLK_LEFT:
|
||||
return Qt::Key_Left;
|
||||
case SDLK_RIGHT:
|
||||
return Qt::Key_Right;
|
||||
case SDLK_UP:
|
||||
return Qt::Key_Up;
|
||||
case SDLK_DOWN:
|
||||
return Qt::Key_Down;
|
||||
case SDLK_CAPSLOCK:
|
||||
return Qt::Key_CapsLock;
|
||||
case SDLK_NUMLOCKCLEAR:
|
||||
return Qt::Key_NumLock;
|
||||
case SDLK_SCROLLLOCK:
|
||||
return Qt::Key_ScrollLock;
|
||||
case SDLK_F1:
|
||||
return Qt::Key_F1;
|
||||
case SDLK_F2:
|
||||
return Qt::Key_F2;
|
||||
case SDLK_F3:
|
||||
return Qt::Key_F3;
|
||||
case SDLK_F4:
|
||||
return Qt::Key_F4;
|
||||
case SDLK_F5:
|
||||
return Qt::Key_F5;
|
||||
case SDLK_F6:
|
||||
return Qt::Key_F6;
|
||||
case SDLK_F7:
|
||||
return Qt::Key_F7;
|
||||
case SDLK_F8:
|
||||
return Qt::Key_F8;
|
||||
case SDLK_F9:
|
||||
return Qt::Key_F9;
|
||||
case SDLK_F10:
|
||||
return Qt::Key_F10;
|
||||
case SDLK_F11:
|
||||
return Qt::Key_F11;
|
||||
case SDLK_F12:
|
||||
return Qt::Key_F12;
|
||||
case SDLK_LSHIFT:
|
||||
return Qt::Key_Shift;
|
||||
case SDLK_LCTRL:
|
||||
return Qt::Key_Control;
|
||||
case SDLK_LALT:
|
||||
return Qt::Key_Alt;
|
||||
case SDLK_LGUI:
|
||||
return Qt::Key_Meta;
|
||||
default:
|
||||
return Qt::Key_unknown;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Keycode KeyboardControlsWindow::convertQtKeyToSDL(Qt::Key qtKey) {
|
||||
switch (qtKey) {
|
||||
case Qt::Key_A:
|
||||
return SDLK_A;
|
||||
case Qt::Key_B:
|
||||
return SDLK_B;
|
||||
case Qt::Key_C:
|
||||
return SDLK_C;
|
||||
case Qt::Key_D:
|
||||
return SDLK_D;
|
||||
case Qt::Key_E:
|
||||
return SDLK_E;
|
||||
case Qt::Key_F:
|
||||
return SDLK_F;
|
||||
case Qt::Key_G:
|
||||
return SDLK_G;
|
||||
case Qt::Key_H:
|
||||
return SDLK_H;
|
||||
case Qt::Key_I:
|
||||
return SDLK_I;
|
||||
case Qt::Key_J:
|
||||
return SDLK_J;
|
||||
case Qt::Key_K:
|
||||
return SDLK_K;
|
||||
case Qt::Key_L:
|
||||
return SDLK_L;
|
||||
case Qt::Key_M:
|
||||
return SDLK_M;
|
||||
case Qt::Key_N:
|
||||
return SDLK_N;
|
||||
case Qt::Key_O:
|
||||
return SDLK_O;
|
||||
case Qt::Key_P:
|
||||
return SDLK_P;
|
||||
case Qt::Key_Q:
|
||||
return SDLK_Q;
|
||||
case Qt::Key_R:
|
||||
return SDLK_R;
|
||||
case Qt::Key_S:
|
||||
return SDLK_S;
|
||||
case Qt::Key_T:
|
||||
return SDLK_T;
|
||||
case Qt::Key_U:
|
||||
return SDLK_U;
|
||||
case Qt::Key_V:
|
||||
return SDLK_V;
|
||||
case Qt::Key_W:
|
||||
return SDLK_W;
|
||||
case Qt::Key_X:
|
||||
return SDLK_X;
|
||||
case Qt::Key_Y:
|
||||
return SDLK_Y;
|
||||
case Qt::Key_Z:
|
||||
return SDLK_Z;
|
||||
case Qt::Key_0:
|
||||
return SDLK_0;
|
||||
case Qt::Key_1:
|
||||
return SDLK_1;
|
||||
case Qt::Key_2:
|
||||
return SDLK_2;
|
||||
case Qt::Key_3:
|
||||
return SDLK_3;
|
||||
case Qt::Key_4:
|
||||
return SDLK_4;
|
||||
case Qt::Key_5:
|
||||
return SDLK_5;
|
||||
case Qt::Key_6:
|
||||
return SDLK_6;
|
||||
case Qt::Key_7:
|
||||
return SDLK_7;
|
||||
case Qt::Key_8:
|
||||
return SDLK_8;
|
||||
case Qt::Key_9:
|
||||
return SDLK_9;
|
||||
case Qt::Key_Space:
|
||||
return SDLK_SPACE;
|
||||
case Qt::Key_Enter:
|
||||
return SDLK_RETURN;
|
||||
case Qt::Key_Return:
|
||||
return SDLK_RETURN;
|
||||
case Qt::Key_Escape:
|
||||
return SDLK_ESCAPE;
|
||||
case Qt::Key_Tab:
|
||||
return SDLK_TAB;
|
||||
case Qt::Key_Backspace:
|
||||
return SDLK_BACKSPACE;
|
||||
case Qt::Key_Delete:
|
||||
return SDLK_DELETE;
|
||||
case Qt::Key_Insert:
|
||||
return SDLK_INSERT;
|
||||
case Qt::Key_Home:
|
||||
return SDLK_HOME;
|
||||
case Qt::Key_End:
|
||||
return SDLK_END;
|
||||
case Qt::Key_PageUp:
|
||||
return SDLK_PAGEUP;
|
||||
case Qt::Key_PageDown:
|
||||
return SDLK_PAGEDOWN;
|
||||
case Qt::Key_Left:
|
||||
return SDLK_LEFT;
|
||||
case Qt::Key_Right:
|
||||
return SDLK_RIGHT;
|
||||
case Qt::Key_Up:
|
||||
return SDLK_UP;
|
||||
case Qt::Key_Down:
|
||||
return SDLK_DOWN;
|
||||
case Qt::Key_CapsLock:
|
||||
return SDLK_CAPSLOCK;
|
||||
case Qt::Key_NumLock:
|
||||
return SDLK_NUMLOCKCLEAR;
|
||||
case Qt::Key_ScrollLock:
|
||||
return SDLK_SCROLLLOCK;
|
||||
case Qt::Key_F1:
|
||||
return SDLK_F1;
|
||||
case Qt::Key_F2:
|
||||
return SDLK_F2;
|
||||
case Qt::Key_F3:
|
||||
return SDLK_F3;
|
||||
case Qt::Key_F4:
|
||||
return SDLK_F4;
|
||||
case Qt::Key_F5:
|
||||
return SDLK_F5;
|
||||
case Qt::Key_F6:
|
||||
return SDLK_F6;
|
||||
case Qt::Key_F7:
|
||||
return SDLK_F7;
|
||||
case Qt::Key_F8:
|
||||
return SDLK_F8;
|
||||
case Qt::Key_F9:
|
||||
return SDLK_F9;
|
||||
case Qt::Key_F10:
|
||||
return SDLK_F10;
|
||||
case Qt::Key_F11:
|
||||
return SDLK_F11;
|
||||
case Qt::Key_F12:
|
||||
return SDLK_F12;
|
||||
case Qt::Key_Shift:
|
||||
return SDLK_LSHIFT;
|
||||
case Qt::Key_Control:
|
||||
return SDLK_LCTRL;
|
||||
case Qt::Key_Alt:
|
||||
return SDLK_LALT;
|
||||
case Qt::Key_Meta:
|
||||
return SDLK_LGUI;
|
||||
default:
|
||||
return SDLK_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardControlsWindow::onEditingFinished() {
|
||||
auto sender = qobject_cast<QKeySequenceEdit*>(QObject::sender());
|
||||
auto new_keySequence = sender->keySequence();
|
||||
|
||||
// If new key sequence is empty (i.e. there is no key assigned to it) - skip 'duplicate' checks
|
||||
// Two checks are needed for the sake of robustness (when we click on a widget but don't type
|
||||
// anything it might no longer be "empty")
|
||||
if (new_keySequence.isEmpty() || new_keySequence.toString().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if sequance is not already used (i.e. making sure there are not duplicates)
|
||||
for (auto& keyEdit : m_listOfKeySequenceEdits) {
|
||||
if (keyEdit != sender && new_keySequence == keyEdit->keySequence()) {
|
||||
sender->clear();
|
||||
sender->setStyleSheet("background-color: red; qproperty-alignment: AlignCenter;");
|
||||
QTimer::singleShot(inputErrorTimerTimeout, sender, [sender]() {
|
||||
sender->setStyleSheet(
|
||||
"QLineEdit { qproperty-alignment: AlignCenter; }"); // Reset to default
|
||||
});
|
||||
|
||||
keyEdit->setStyleSheet("background-color: red; qproperty-alignment: AlignCenter;");
|
||||
QTimer::singleShot(inputErrorTimerTimeout, keyEdit, [keyEdit]() {
|
||||
keyEdit->setStyleSheet(
|
||||
"QLineEdit { qproperty-alignment: AlignCenter; }"); // Reset to default
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
40
src/qt_gui/keyboardcontrolswindow.h
Normal file
40
src/qt_gui/keyboardcontrolswindow.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QKeySequenceEdit>
|
||||
#include <QMainWindow>
|
||||
|
||||
#include <SDL3/SDL_keycode.h>
|
||||
#include "input/keys_constants.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class KeyboardControlsWindow;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class KeyboardControlsWindow : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
KeyboardControlsWindow(QWidget* parent = nullptr);
|
||||
~KeyboardControlsWindow();
|
||||
|
||||
const std::map<u32, KeysMapping>& getKeysMapping() const;
|
||||
|
||||
private slots:
|
||||
void onEditingFinished();
|
||||
|
||||
private:
|
||||
void validateAndSaveKeyBindings();
|
||||
SDL_Keycode convertQtKeyToSDL(Qt::Key qtKey);
|
||||
Qt::Key convertSDLKeyToQt(SDL_Keycode qtKey);
|
||||
|
||||
Ui::KeyboardControlsWindow* ui;
|
||||
QSet<QKeySequenceEdit*> m_listOfKeySequenceEdits;
|
||||
std::map<u32, KeysMapping> m_keysMap;
|
||||
std::map<KeysMapping, u32> m_reverseKeysMap;
|
||||
};
|
439
src/qt_gui/keyboardcontrolswindow.ui
Normal file
439
src/qt_gui/keyboardcontrolswindow.ui
Normal file
|
@ -0,0 +1,439 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
<ui version="4.0">
|
||||
<class>KeyboardControlsWindow</class>
|
||||
<widget class="QDialog" name="KeyboardControlsWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1155</width>
|
||||
<height>790</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>1148</width>
|
||||
<height>731</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>1155</width>
|
||||
<height>790</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Configure Controller Bindings</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>1131</width>
|
||||
<height>711</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="port_1">
|
||||
<attribute name="title">
|
||||
<string>Port 1</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="centralwidget" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>-10</x>
|
||||
<y>20</y>
|
||||
<width>1131</width>
|
||||
<height>621</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QWidget" name="bgWidget" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>170</x>
|
||||
<y>50</y>
|
||||
<width>870</width>
|
||||
<height>522</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"> QWidget { background-image: url(:images/PS4_controller_scheme.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center; }
|
||||
</string>
|
||||
</property>
|
||||
<widget class="QKeySequenceEdit" name="StartKeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>550</x>
|
||||
<y>0</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="SelectKeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>260</x>
|
||||
<y>0</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="LAnalogDownkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>280</x>
|
||||
<y>480</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="LAnalogLeftkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>240</x>
|
||||
<y>440</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="LAnalogUpkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>280</x>
|
||||
<y>400</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="LAnalogRightkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>320</x>
|
||||
<y>440</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="PSkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>400</x>
|
||||
<y>400</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="RAnalogDownkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>520</x>
|
||||
<y>480</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="RAnalogLeftkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>480</x>
|
||||
<y>440</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="RAnalogUpkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>520</x>
|
||||
<y>400</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="RAnalogRightkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>560</x>
|
||||
<y>440</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="DPadLeftkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>230</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="DPadRightkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>90</x>
|
||||
<y>230</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="DPadUpkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>190</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="DPadDownkeySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>270</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="L2keySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>90</x>
|
||||
<y>40</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="L1keySequenceEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>90</x>
|
||||
<y>110</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="CrossKeySequenceEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1050</x>
|
||||
<y>380</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="R2KeySequenceEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1050</x>
|
||||
<y>30</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="CircleKeySequenceEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1050</x>
|
||||
<y>240</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="R1KeySequenceEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1050</x>
|
||||
<y>100</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="SquareKeySequenceEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1050</x>
|
||||
<y>310</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QKeySequenceEdit" name="TriangleKeySequenceEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1050</x>
|
||||
<y>170</y>
|
||||
<width>71</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSequenceLength">
|
||||
<longLong>1</longLong>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="port_2">
|
||||
<attribute name="title">
|
||||
<string>Port 2</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="applyButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1030</x>
|
||||
<y>740</y>
|
||||
<width>100</width>
|
||||
<height>32</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Apply</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>890</x>
|
||||
<y>740</y>
|
||||
<width>100</width>
|
||||
<height>32</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -50,6 +50,7 @@ bool MainWindow::Init() {
|
|||
auto end = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||
statusBar.reset(new QStatusBar);
|
||||
m_controllerControlsDialog.reset(new KeyboardControlsWindow());
|
||||
this->setStatusBar(statusBar.data());
|
||||
// Update status bar
|
||||
int numGames = m_game_info->m_games.size();
|
||||
|
@ -90,6 +91,9 @@ void MainWindow::AddUiWidgets() {
|
|||
ui->toolBar->addWidget(ui->stopButton);
|
||||
ui->toolBar->addWidget(ui->refreshButton);
|
||||
ui->toolBar->addWidget(ui->settingsButton);
|
||||
auto connection = QObject::connect(ui->controllerButton, &QPushButton::clicked, this,
|
||||
&MainWindow::ControllerConfigurationButtonPressed);
|
||||
|
||||
ui->toolBar->addWidget(ui->controllerButton);
|
||||
QFrame* line = new QFrame(this);
|
||||
line->setFrameShape(QFrame::StyledPanel);
|
||||
|
@ -99,6 +103,10 @@ void MainWindow::AddUiWidgets() {
|
|||
ui->toolBar->addWidget(ui->mw_searchbar);
|
||||
}
|
||||
|
||||
void MainWindow::ControllerConfigurationButtonPressed() {
|
||||
m_controllerControlsDialog->show();
|
||||
}
|
||||
|
||||
void MainWindow::CreateDockWindows() {
|
||||
// place holder widget is needed for good health they say :)
|
||||
QWidget* phCentralWidget = new QWidget(this);
|
||||
|
@ -781,6 +789,10 @@ void MainWindow::InstallDirectory() {
|
|||
RefreshGameTable();
|
||||
}
|
||||
|
||||
std::map<u32, KeysMapping> MainWindow::getKeysMapping() {
|
||||
return m_controllerControlsDialog->getKeysMapping();
|
||||
}
|
||||
|
||||
void MainWindow::SetLastUsedTheme() {
|
||||
Theme lastTheme = static_cast<Theme>(Config::getMainWindowTheme());
|
||||
m_window_themes.SetWindowTheme(lastTheme, ui->mw_searchbar);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "game_info.h"
|
||||
#include "game_list_frame.h"
|
||||
#include "game_list_utils.h"
|
||||
#include "keyboardcontrolswindow.h"
|
||||
#include "main_window_themes.h"
|
||||
#include "main_window_ui.h"
|
||||
#include "pkg_viewer.h"
|
||||
|
@ -37,6 +38,8 @@ public:
|
|||
void InstallDirectory();
|
||||
void StartGame();
|
||||
|
||||
std::map<u32, KeysMapping> getKeysMapping();
|
||||
|
||||
private Q_SLOTS:
|
||||
void ConfigureGuiFromSettings();
|
||||
void SaveWindowState() const;
|
||||
|
@ -45,6 +48,7 @@ private Q_SLOTS:
|
|||
void RefreshGameTable();
|
||||
void HandleResize(QResizeEvent* event);
|
||||
void OnLanguageChanged(const std::string& locale);
|
||||
void ControllerConfigurationButtonPressed();
|
||||
|
||||
private:
|
||||
Ui_MainWindow* ui;
|
||||
|
@ -80,6 +84,7 @@ private:
|
|||
QScopedPointer<ElfViewer> m_elf_viewer;
|
||||
// Status Bar.
|
||||
QScopedPointer<QStatusBar> statusBar;
|
||||
QScopedPointer<KeyboardControlsWindow> m_controllerControlsDialog;
|
||||
// Available GPU devices
|
||||
std::vector<QString> m_physical_devices;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue