all repos — mgba @ 81e65c08d80e39d6ffa199c345f891832ebbae04

mGBA Game Boy Advance Emulator

Qt: Add keyboard remapper
Jeffrey Pfau jeffrey@endrift.com
Fri, 07 Nov 2014 03:11:44 -0800
commit

81e65c08d80e39d6ffa199c345f891832ebbae04

parent

0efe9881610c9935dfd20e85c082b9d03a6ca866

A res/keymap.svg

@@ -0,0 +1,136 @@

+<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="480px" height="480px" viewBox="0 0 480 480" enable-background="new 0 0 480 480" xml:space="preserve"> +<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="239.9995" y1="0" x2="239.9995" y2="480.0005"> + <stop offset="0" style="stop-color:#7A65F5"/> + <stop offset="1" style="stop-color:#302575"/> +</linearGradient> +<path fill="url(#SVGID_1_)" d="M480,450c0,16.5684-13.4316,30-30,30H30c-16.5684,0-30-13.4316-30-30V30C0,13.4316,13.4316,0,30,0 + h420c16.5684,0,30,13.4316,30,30V450z"/> +<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="239.9995" y1="-479" x2="239.9994" y2="1159.0004"> + <stop offset="0" style="stop-color:#7A65F5"/> + <stop offset="1" style="stop-color:#302575"/> +</linearGradient> +<path fill="url(#SVGID_2_)" d="M30,473c-12.6821,0-23-10.3174-23-23V30C7,17.3179,17.3179,7,30,7h420c12.6826,0,23,10.3179,23,23 + v420c0,12.6826-10.3174,23-23,23H30z"/> +<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="118.3335" y1="291.667" x2="118.3335" y2="47.9991"> + <stop offset="0" style="stop-color:#7A65F5"/> + <stop offset="1" style="stop-color:#302575"/> +</linearGradient> +<path fill="url(#SVGID_3_)" d="M118.3335,122.333c-46.7603,0-84.667,37.9067-84.667,84.667c0,46.7607,37.9067,84.667,84.667,84.667 + s84.667-37.9062,84.667-84.667C203.0005,160.2397,165.0938,122.333,118.3335,122.333z M118.334,278.7344 + c-39.6172,0-71.7339-32.1172-71.7339-71.7344c0-39.6177,32.1167-71.7339,71.7339-71.7339S190.0679,167.3823,190.0679,207 + C190.0679,246.6172,157.9512,278.7344,118.334,278.7344z"/> +<g> + + <radialGradient id="SVGID_4_" cx="118.8335" cy="138.167" r="103.2725" fx="165.1467" fy="138.167" gradientTransform="matrix(-4.371139e-08 1 -1.42 -6.206884e-08 315.0264 19.3335)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#9C9CB3"/> + <stop offset="1" style="stop-color:#333045"/> + </radialGradient> + <path fill="url(#SVGID_4_)" d="M172.1973,207c0-9.667-3.0029-17.333-3.0029-17.333H135.667v-33.5283 + c0,0-7.667-3.0029-17.3335-3.0029s-17.333,3.0029-17.333,3.0029v33.5283H67.4727c0,0-3.0029,7.666-3.0029,17.333 + c0,9.666,3.0029,17.333,3.0029,17.333h33.5278v33.5273c0,0,7.6665,3.0039,17.333,3.0039s17.3335-3.0039,17.3335-3.0039V224.333 + h33.5273C169.1943,224.333,172.1973,216.666,172.1973,207z"/> + <path fill="#5C567D" d="M118.3335,258.8643c-6.9185,0-12.833-1.625-15.333-2.4287V222.333h-34.103 + c-0.8027-2.5-2.4277-8.4146-2.4277-15.333s1.6245-12.833,2.4277-15.333h34.103v-34.1035c2.5-0.8027,8.4146-2.4277,15.333-2.4277 + c6.9453,0,12.8408,1.6226,15.3335,2.4258v34.1055h34.1025c0.8032,2.5,2.4277,8.4146,2.4277,15.333 + c0,6.9448-1.6226,12.8403-2.4258,15.333H133.667v34.1025C131.167,257.2393,125.252,258.8643,118.3335,258.8643z"/> + <radialGradient id="SVGID_5_" cx="118.333" cy="220.397" r="69.7522" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#706F8A"/> + <stop offset="0.9951" style="stop-color:#2D2842"/> + </radialGradient> + <circle fill="url(#SVGID_5_)" cx="118.333" cy="207" r="21.2749"/> +</g> +<g> + <g> + <linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="198.9673" y1="490" x2="198.9673" y2="384.981"> + <stop offset="0" style="stop-color:#7A65F5"/> + <stop offset="1" style="stop-color:#302575"/> + </linearGradient> + <path fill="url(#SVGID_6_)" d="M198.9678,387.0303c-18.8418,0-34.1162,15.2734-34.1162,34.1162 + c0,18.8418,15.2744,34.1152,34.1162,34.1152c18.8408,0,34.1152-15.2734,34.1152-34.1152 + C233.083,402.3037,217.8086,387.0303,198.9678,387.0303z M198.9678,445.7871c-13.6089,0-24.6401-11.0332-24.6401-24.6406 + c0-13.6094,11.0312-24.6406,24.6401-24.6406c13.6074,0,24.6396,11.0312,24.6396,24.6406 + C223.6074,434.7539,212.5752,445.7871,198.9678,445.7871z"/> + <g> + <linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="198.9673" y1="437.4414" x2="198.9673" y2="404.8516"> + <stop offset="0" style="stop-color:#333045"/> + <stop offset="1" style="stop-color:#9C9CB3"/> + </linearGradient> + <circle fill="url(#SVGID_7_)" cx="198.9673" cy="421.1465" r="16.2949"/> + <circle fill="#5C567D" cx="198.9673" cy="421.1465" r="15.0737"/> + </g> + </g> + <g> + <linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="281.0312" y1="490" x2="281.0312" y2="384.981"> + <stop offset="0" style="stop-color:#7A65F5"/> + <stop offset="1" style="stop-color:#302575"/> + </linearGradient> + <path fill="url(#SVGID_8_)" d="M281.0322,387.0303c-18.8418,0-34.1162,15.2734-34.1162,34.1162 + c0,18.8418,15.2744,34.1152,34.1162,34.1152c18.8408,0,34.1152-15.2734,34.1152-34.1152 + C315.1475,402.3037,299.873,387.0303,281.0322,387.0303z M281.0322,445.7871c-13.6084,0-24.6396-11.0332-24.6396-24.6406 + c0-13.6094,11.0312-24.6406,24.6396-24.6406c13.6074,0,24.6396,11.0312,24.6396,24.6406 + C305.6719,434.7539,294.6396,445.7871,281.0322,445.7871z"/> + <g> + <linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="281.0322" y1="437.4414" x2="281.0322" y2="404.8516"> + <stop offset="0" style="stop-color:#333045"/> + <stop offset="1" style="stop-color:#9C9CB3"/> + </linearGradient> + <circle fill="url(#SVGID_9_)" cx="281.0322" cy="421.1465" r="16.2949"/> + <circle fill="#5C567D" cx="281.0317" cy="421.1465" r="15.0737"/> + </g> + </g> +</g> +<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="356.1787" y1="280.2178" x2="356.1787" y2="134.9952"> + <stop offset="0" style="stop-color:#7A65F5"/> + <stop offset="1" style="stop-color:#302575"/> +</linearGradient> +<path fill="url(#SVGID_10_)" d="M437.9521,179.5503c-7.0088-23.9434-32.0986-37.6724-56.043-30.6646l-76.8369,22.4912 + c-23.9453,7.0073-37.6748,32.0991-30.666,56.043c7.0088,23.9443,32.0996,37.6719,56.0449,30.6621l76.8369-22.4902 + C431.2314,228.585,444.96,203.4946,437.9521,179.5503z M426.3311,209.6025c-4.6377,8.4756-12.2979,14.6382-21.5703,17.3516 + l-76.8379,22.4902c-3.3301,0.9746-6.7559,1.4688-10.1816,1.4688c-0.001,0-0.001,0-0.002,0 + c-15.9443-0.001-30.2109-10.7012-34.6953-26.0215c-2.7148-9.2729-1.6553-19.0479,2.9824-27.5244 + c4.6387-8.4761,12.2998-14.6387,21.5732-17.3525l76.8379-22.4912c3.3281-0.9741,6.7539-1.4683,10.1807-1.4683 + c15.9434,0,30.2109,10.7012,34.6963,26.0234C432.0283,191.3516,430.9688,201.1265,426.3311,209.6025z"/> +<g> + <linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="395.9062" y1="217.8188" x2="395.9062" y2="166.6519"> + <stop offset="0" style="stop-color:#333045"/> + <stop offset="1" style="stop-color:#9C9CB3"/> + </linearGradient> + <circle fill="url(#SVGID_11_)" cx="395.9062" cy="192.2358" r="25.584"/> + <circle fill="#5C567D" cx="395.9072" cy="192.2358" r="23.666"/> +</g> +<g> + <linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="318.9785" y1="239.6406" x2="318.9785" y2="188.4722"> + <stop offset="0" style="stop-color:#333045"/> + <stop offset="1" style="stop-color:#9C9CB3"/> + </linearGradient> + <circle fill="url(#SVGID_12_)" cx="318.979" cy="214.0571" r="25.5835"/> + <circle fill="#5C567D" cx="318.98" cy="214.0571" r="23.6665"/> +</g> +<g> + <linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="-15.0425" y1="77.9346" x2="55.6245" y2="-5.3989"> + <stop offset="0" style="stop-color:#333045"/> + <stop offset="1" style="stop-color:#9C9CB3"/> + </linearGradient> + <path fill="url(#SVGID_13_)" d="M33.6665,0H61.5c2.9697,1.0112,2.3306,7,2.3306,7l0.0581,8.4844 + C63.8887,31.3535,48.9922,50,27.9922,50H7.0068c0,0-5.2197,2.312-7.0068-8.0029V30C0,11.5,12.333,0,33.6665,0z"/> + <path fill="#5C567D" d="M9.7783,49.1743c-0.2793-0.251-1.166-1.2764-1.7744-4.5308V33.0029c0-16.5234,10.813-26,29.6665-26h26.0854 + c0.1318,0.709,0.1821,1.7549,0.0996,2.5908L63.835,9.8032l0.0576,8.7114c0,14.3774-13.6406,30.4883-31.8965,30.4883H10.1646 + L9.7783,49.1743z"/> +</g> +<g> + + <linearGradient id="SVGID_14_" gradientUnits="userSpaceOnUse" x1="401.0615" y1="77.9336" x2="471.7285" y2="-5.3999" gradientTransform="matrix(-1 0 0 1 896.1055 0)"> + <stop offset="0" style="stop-color:#333045"/> + <stop offset="1" style="stop-color:#9C9CB3"/> + </linearGradient> + <path fill="url(#SVGID_14_)" d="M446.334,0h-27.833c-2.9697,1.0112-2.3311,7-2.3311,7l-0.0576,8.4844 + C416.1123,31.3535,431.0088,50,452.0088,50h20.9854c0,0,5.2197,2.312,7.0068-8.0029V30C480.001,11.5,467.668,0,446.334,0z"/> + <path fill="#5C567D" d="M470.2227,49.1743c0.2793-0.251,1.166-1.2764,1.7744-4.5308V33.0029c0-16.5234-10.8135-26-29.667-26 + h-26.085c-0.1318,0.709-0.1826,1.7549-0.0996,2.5908l0.0205,0.2095l-0.0576,8.7114c0,14.3774,13.6406,30.4883,31.8965,30.4883 + h21.8311L470.2227,49.1743z"/> +</g> +</svg>
M src/gba/gba-input.csrc/gba/gba-input.c

@@ -109,6 +109,27 @@ }

impl->map[input] = key; } +int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) { + if (input >= GBA_KEY_MAX) { + return 0; + } + + size_t m; + const struct GBAInputMapImpl* impl = 0; + for (m = 0; m < map->numMaps; ++m) { + if (map->maps[m].type == type) { + impl = &map->maps[m]; + break; + } + } + if (!impl || !impl->map) { + return 0; + } + + return impl->map[input]; +} + + void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) { _loadKey(map, type, config, GBA_KEY_A, "A"); _loadKey(map, type, config, GBA_KEY_B, "B");
M src/gba/gba-input.hsrc/gba/gba-input.h

@@ -15,6 +15,7 @@ void GBAInputMapDeinit(struct GBAInputMap*);

enum GBAKey GBAInputMapKey(const struct GBAInputMap*, uint32_t type, int key); void GBAInputBindKey(struct GBAInputMap*, uint32_t type, int key, enum GBAKey input); +int GBAInputQueryBinding(const struct GBAInputMap*, uint32_t type, enum GBAKey input); void GBAInputMapLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*);
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -34,8 +34,10 @@ AudioProcessor.cpp

ConfigController.cpp Display.cpp GBAApp.cpp + GBAKeyEditor.cpp GameController.cpp InputController.cpp + KeyEditor.cpp LoadSaveState.cpp LogView.cpp SavestateButton.cpp
A src/platform/qt/GBAKeyEditor.cpp

@@ -0,0 +1,171 @@

+#include "GBAKeyEditor.h" + +#include <QPaintEvent> +#include <QPainter> +#include <QPicture> +#include <QPushButton> + +#include "InputController.h" +#include "KeyEditor.h" + +extern "C" { +#include "gba-input.h" +} + +using namespace QGBA; + +const qreal GBAKeyEditor::DPAD_CENTER_X = 0.247; +const qreal GBAKeyEditor::DPAD_CENTER_Y = 0.431; +const qreal GBAKeyEditor::DPAD_WIDTH = 0.1; +const qreal GBAKeyEditor::DPAD_HEIGHT = 0.1; + +GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, QWidget* parent) + : QWidget(parent) + , m_background(QString(":/res/keymap.png")) +{ + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint); + setMinimumSize(300, 300); + + const GBAInputMap* map = controller->map(); + + m_keyDU = new KeyEditor(this); + m_keyDD = new KeyEditor(this); + m_keyDL = new KeyEditor(this); + m_keyDR = new KeyEditor(this); + m_keySelect = new KeyEditor(this); + m_keyStart = new KeyEditor(this); + m_keyA = new KeyEditor(this); + m_keyB = new KeyEditor(this); + m_keyL = new KeyEditor(this); + m_keyR = new KeyEditor(this); + m_keyDU->setValue(GBAInputQueryBinding(map, type, GBA_KEY_UP)); + m_keyDD->setValue(GBAInputQueryBinding(map, type, GBA_KEY_DOWN)); + m_keyDL->setValue(GBAInputQueryBinding(map, type, GBA_KEY_LEFT)); + m_keyDR->setValue(GBAInputQueryBinding(map, type, GBA_KEY_RIGHT)); + m_keySelect->setValue(GBAInputQueryBinding(map, type, GBA_KEY_SELECT)); + m_keyStart->setValue(GBAInputQueryBinding(map, type, GBA_KEY_START)); + m_keyA->setValue(GBAInputQueryBinding(map, type, GBA_KEY_A)); + m_keyB->setValue(GBAInputQueryBinding(map, type, GBA_KEY_B)); + m_keyL->setValue(GBAInputQueryBinding(map, type, GBA_KEY_L)); + m_keyR->setValue(GBAInputQueryBinding(map, type, GBA_KEY_R)); + + connect(m_keyDU, &KeyEditor::valueChanged, [this, type, controller](int key) { + controller->bindKey(type, key, GBA_KEY_UP); + setNext(); + }); + + connect(m_keyDD, &KeyEditor::valueChanged, [this, type, controller](int key) { + controller->bindKey(type, key, GBA_KEY_DOWN); + setNext(); + }); + + connect(m_keyDL, &KeyEditor::valueChanged, [this, type, controller](int key) { + controller->bindKey(type, key, GBA_KEY_LEFT); + setNext(); + }); + + connect(m_keyDR, &KeyEditor::valueChanged, [this, type, controller](int key) { + controller->bindKey(type, key, GBA_KEY_RIGHT); + setNext(); + }); + + connect(m_keySelect, &KeyEditor::valueChanged, [this, type, controller](int key) { + controller->bindKey(type, key, GBA_KEY_SELECT); + setNext(); + }); + + connect(m_keyStart, &KeyEditor::valueChanged, [this, type, controller](int key) { + controller->bindKey(type, key, GBA_KEY_START); + setNext(); + }); + + connect(m_keyA, &KeyEditor::valueChanged, [this, type, controller](int key) { + controller->bindKey(type, key, GBA_KEY_A); + setNext(); + }); + + connect(m_keyB, &KeyEditor::valueChanged, [this, type, controller](int key) { + controller->bindKey(type, key, GBA_KEY_B); + setNext(); + }); + + connect(m_keyL, &KeyEditor::valueChanged, [this, type, controller](int key) { + controller->bindKey(type, key, GBA_KEY_L); + setNext(); + }); + + connect(m_keyR, &KeyEditor::valueChanged, [this, type, controller](int key) { + controller->bindKey(type, key, GBA_KEY_R); + setNext(); + }); + + m_setAll = new QPushButton(tr("Set all"), this); + connect(m_setAll, SIGNAL(pressed()), this, SLOT(setAll())); + + m_keyOrder = QList<KeyEditor*>{ + m_keyDU, + m_keyDR, + m_keyDD, + m_keyDL, + m_keyA, + m_keyB, + m_keySelect, + m_keyStart, + m_keyL, + m_keyR + }; + + m_currentKey = m_keyOrder.end(); + + QPixmap background(":/res/keymap.png"); + m_background = background.scaled(QSize(300, 300) * devicePixelRatio(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + m_background.setDevicePixelRatio(devicePixelRatio()); +} + +void GBAKeyEditor::setAll() { + m_currentKey = m_keyOrder.begin(); + (*m_currentKey)->setFocus(); +} + +void GBAKeyEditor::resizeEvent(QResizeEvent* event) { + setLocation(m_setAll, 0.5, 0.2); + setLocation(m_keyDU, DPAD_CENTER_X, DPAD_CENTER_Y - DPAD_HEIGHT); + setLocation(m_keyDD, DPAD_CENTER_X, DPAD_CENTER_Y + DPAD_HEIGHT); + setLocation(m_keyDL, DPAD_CENTER_X - DPAD_WIDTH, DPAD_CENTER_Y); + setLocation(m_keyDR, DPAD_CENTER_X + DPAD_WIDTH, DPAD_CENTER_Y); + setLocation(m_keySelect, 0.415, 0.93); + setLocation(m_keyStart, 0.585, 0.93); + setLocation(m_keyA, 0.826, 0.451); + setLocation(m_keyB, 0.667, 0.490); + setLocation(m_keyL, 0.1, 0.1); + setLocation(m_keyR, 0.9, 0.1); +} + +void GBAKeyEditor::paintEvent(QPaintEvent* event) { + QPainter painter(this); + painter.drawPixmap(0, 0, m_background); +} + +void GBAKeyEditor::setNext() { + if (m_currentKey == m_keyOrder.end()) { + return; + } + + if (!(*m_currentKey)->hasFocus()) { + m_currentKey = m_keyOrder.end(); + } + + ++m_currentKey; + if (m_currentKey != m_keyOrder.end()) { + (*m_currentKey)->setFocus(); + } else { + (*(m_currentKey - 1))->clearFocus(); + } +} + +void GBAKeyEditor::setLocation(QWidget* widget, qreal x, qreal y) { + QSize s = size(); + QSize hint = widget->sizeHint(); + widget->setGeometry(s.width() * x - hint.width() / 2.0, s.height() * y - hint.height() / 2.0, hint.width(), hint.height()); +}
A src/platform/qt/GBAKeyEditor.h

@@ -0,0 +1,59 @@

+#ifndef QGBA_GBA_KEY_EDITOR +#define QGBA_GBA_KEY_EDITOR + +#include <QList> +#include <QPixmap> +#include <QWidget> + +class QPushButton; + +namespace QGBA { + +class InputController; +class KeyEditor; + +class GBAKeyEditor : public QWidget { +Q_OBJECT + +public: + GBAKeyEditor(InputController* controller, int type, QWidget* parent = nullptr); + +public slots: + void setAll(); + +protected: + virtual void resizeEvent(QResizeEvent*) override; + virtual void paintEvent(QPaintEvent*) override; + +private: + static const qreal DPAD_CENTER_X; + static const qreal DPAD_CENTER_Y; + static const qreal DPAD_WIDTH; + static const qreal DPAD_HEIGHT; + + void setNext(); + + void setLocation(QWidget* widget, qreal x, qreal y); + + QPushButton* m_setAll; + KeyEditor* m_keyDU; + KeyEditor* m_keyDD; + KeyEditor* m_keyDL; + KeyEditor* m_keyDR; + KeyEditor* m_keySelect; + KeyEditor* m_keyStart; + KeyEditor* m_keyA; + KeyEditor* m_keyB; + KeyEditor* m_keyL; + KeyEditor* m_keyR; + QList<KeyEditor*> m_keyOrder; + QList<KeyEditor*>::iterator m_currentKey; + + InputController* m_controller; + + QPixmap m_background; +}; + +} + +#endif
M src/platform/qt/InputController.cppsrc/platform/qt/InputController.cpp

@@ -52,6 +52,10 @@ GBAKey InputController::mapKeyboard(int key) const {

return GBAInputMapKey(&m_inputMap, KEYBOARD, key); } +void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) { + return GBAInputBindKey(&m_inputMap, type, key, gbaKey); +} + #ifdef BUILD_SDL int InputController::testSDLEvents() { SDL_Joystick* joystick = m_sdlEvents.joystick;
M src/platform/qt/InputController.hsrc/platform/qt/InputController.h

@@ -25,6 +25,10 @@ void loadConfiguration(uint32_t type, const Configuration* config);

GBAKey mapKeyboard(int key) const; + void bindKey(uint32_t type, int key, GBAKey); + + const GBAInputMap* map() const { return &m_inputMap; } + #ifdef BUILD_SDL int testSDLEvents(); #endif
A src/platform/qt/KeyEditor.cpp

@@ -0,0 +1,28 @@

+#include "KeyEditor.h" + +#include <QKeyEvent> + +using namespace QGBA; + +KeyEditor::KeyEditor(QWidget* parent) + : QLineEdit(parent) +{ + setAlignment(Qt::AlignCenter); +} + +void KeyEditor::setValue(int key) { + setText(QKeySequence(key).toString(QKeySequence::NativeText)); + m_key = key; + emit valueChanged(key); +} + +QSize KeyEditor::sizeHint() const { + QSize hint = QLineEdit::sizeHint(); + hint.setWidth(40); + return hint; +} + +void KeyEditor::keyPressEvent(QKeyEvent* event) { + setValue(event->key()); + event->accept(); +}
A src/platform/qt/KeyEditor.h

@@ -0,0 +1,31 @@

+#ifndef QGBA_KEY_EDITOR +#define QGBA_KEY_EDITOR + +#include <QLineEdit> + +namespace QGBA { + +class KeyEditor : public QLineEdit { +Q_OBJECT + +public: + KeyEditor(QWidget* parent = nullptr); + + void setValue(int key); + int value() const { return m_key; } + + virtual QSize sizeHint() const override; + +signals: + void valueChanged(int key); + +protected: + virtual void keyPressEvent(QKeyEvent* event) override; + +private: + int m_key; +}; + +} + +#endif
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -8,6 +8,7 @@ #include <QStackedLayout>

#include "ConfigController.h" #include "GameController.h" +#include "GBAKeyEditor.h" #include "GDBController.h" #include "GDBWindow.h" #include "LoadSaveState.h"

@@ -134,6 +135,13 @@ QString filename = QFileDialog::getOpenFileName(this, tr("Select patch"), QString(), tr("Patches (*.ips *.ups)"));

if (!filename.isEmpty()) { m_controller->loadPatch(filename); } +} + +void Window::openKeymapWindow() { + GBAKeyEditor* keyEditor = new GBAKeyEditor(&m_inputController, InputController::KEYBOARD); + connect(this, SIGNAL(shutdown()), keyEditor, SLOT(close())); + keyEditor->setAttribute(Qt::WA_DeleteOnClose); + keyEditor->show(); } #ifdef USE_FFMPEG

@@ -394,6 +402,11 @@ ConfigOption* audioSync = m_config->addOption("audioSync");

audioSync->addBoolean(tr("Sync to &audio"), emulationMenu); audioSync->connect([this](const QVariant& value) { m_controller->setAudioSync(value.toBool()); }); m_config->updateOption("audioSync"); + + emulationMenu->addSeparator(); + QAction* keymap = new QAction(tr("Remap keyboard..."), emulationMenu); + connect(keymap, SIGNAL(triggered()), this, SLOT(openKeymapWindow())); + emulationMenu->addAction(keymap); QMenu* videoMenu = menubar->addMenu(tr("&Video")); QMenu* frameMenu = videoMenu->addMenu(tr("Frame size"));
M src/platform/qt/Window.hsrc/platform/qt/Window.h

@@ -50,6 +50,8 @@ void toggleFullScreen();

void loadConfig(); void saveConfig(); + void openKeymapWindow(); + #ifdef USE_FFMPEG void openVideoWindow(); #endif
M src/platform/qt/resources.qrcsrc/platform/qt/resources.qrc

@@ -1,5 +1,6 @@

<!DOCTYPE RCC><RCC version="1.0"> <qresource> <file>../../../res/mgba-1024.png</file> + <file>../../../res/keymap.png</file> </qresource> </RCC>