Redesign the Input dialog (#1226)

This commit is contained in:
Rayyan Ansari 2021-09-30 17:23:25 +00:00 committed by GitHub
parent 737171c906
commit 796d603332
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 3110 additions and 698 deletions

2
.gitignore vendored
View File

@ -9,7 +9,9 @@ melon_grc.h
melon.rc melon.rc
cmake-build cmake-build
cmake-build-debug cmake-build-debug
compile_commands.json
.idea .idea
.cache
*.exe *.exe

View File

@ -126,10 +126,14 @@ If everything went well, melonDS.app should now be in the current directory.
* limittox for the icon * limittox for the icon
* All of you comrades who have been testing melonDS, reporting issues, suggesting shit, etc * All of you comrades who have been testing melonDS, reporting issues, suggesting shit, etc
## License ## Licenses
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html) [![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html)
melonDS is free software: you can redistribute it and/or modify melonDS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
### External
* Images used in the Input Config Dialog - see `src/frontend/qt_sdl/InputConfig/resources/LICENSE.md`

View File

@ -5,7 +5,9 @@ SET(SOURCES_QT_SDL
main_shaders.h main_shaders.h
CheatsDialog.cpp CheatsDialog.cpp
EmuSettingsDialog.cpp EmuSettingsDialog.cpp
InputConfigDialog.cpp InputConfig/InputConfigDialog.cpp
InputConfig/MapButton.h
InputConfig/resources/ds.qrc
VideoSettingsDialog.cpp VideoSettingsDialog.cpp
AudioSettingsDialog.cpp AudioSettingsDialog.cpp
WifiSettingsDialog.cpp WifiSettingsDialog.cpp
@ -97,6 +99,7 @@ target_link_libraries(melonDS core)
if (BUILD_STATIC) if (BUILD_STATIC)
target_link_libraries(melonDS -static ${SDL2_STATIC_LIBRARIES} ${SLIRP_STATIC_LIBRARIES} ${LIBARCHIVE_STATIC_LIBRARIES}) target_link_libraries(melonDS -static ${SDL2_STATIC_LIBRARIES} ${SLIRP_STATIC_LIBRARIES} ${LIBARCHIVE_STATIC_LIBRARIES})
qt_import_plugins(melonDS INCLUDE Qt::QSvgPlugin)
else() else()
target_link_libraries(melonDS ${SDL2_LIBRARIES} ${SLIRP_LIBRARIES} ${LIBARCHIVE_LIBRARIES}) target_link_libraries(melonDS ${SDL2_LIBRARIES} ${SLIRP_LIBRARIES} ${LIBARCHIVE_LIBRARIES})
endif() endif()

View File

@ -0,0 +1,253 @@
/*
Copyright 2016-2021 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <QGroupBox>
#include <QLabel>
#include <QKeyEvent>
#include <QDebug>
#include <SDL2/SDL.h>
#include "types.h"
#include "Config.h"
#include "PlatformConfig.h"
#include "MapButton.h"
#include "Input.h"
#include "InputConfigDialog.h"
#include "ui_InputConfigDialog.h"
InputConfigDialog* InputConfigDialog::currentDlg = nullptr;
const int dskeyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 2, 3};
const char* dskeylabels[12] = {"A", "B", "X", "Y", "Left", "Right", "Up", "Down", "L", "R", "Select", "Start"};
const int hk_addons[] =
{
HK_SolarSensorIncrease,
HK_SolarSensorDecrease,
};
const char* hk_addons_labels[] =
{
"[Boktai] Sunlight + ",
"[Boktai] Sunlight - ",
};
const int hk_general[] =
{
HK_Pause,
HK_Reset,
HK_FrameStep,
HK_FastForward,
HK_FastForwardToggle,
HK_FullscreenToggle,
HK_Lid,
HK_Mic,
HK_SwapScreens
};
const char* hk_general_labels[] =
{
"Pause/resume",
"Reset",
"Frame step",
"Fast forward",
"Toggle FPS limit",
"Toggle Fullscreen",
"Close/open lid",
"Microphone",
"Swap screens"
};
const int keypad_num = 12;
const int hk_addons_num = 2;
const int hk_general_num = 9;
InputConfigDialog::InputConfigDialog(QWidget* parent) : QDialog(parent), ui(new Ui::InputConfigDialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
for (int i = 0; i < keypad_num; i++)
{
keypadKeyMap[i] = Config::KeyMapping[dskeyorder[i]];
keypadJoyMap[i] = Config::JoyMapping[dskeyorder[i]];
}
for (int i = 0; i < hk_addons_num; i++)
{
addonsKeyMap[i] = Config::HKKeyMapping[hk_addons[i]];
addonsJoyMap[i] = Config::HKJoyMapping[hk_addons[i]];
}
for (int i = 0; i < hk_general_num; i++)
{
hkGeneralKeyMap[i] = Config::HKKeyMapping[hk_general[i]];
hkGeneralJoyMap[i] = Config::HKJoyMapping[hk_general[i]];
}
populatePage(ui->tabAddons, hk_addons_num, hk_addons_labels, addonsKeyMap, addonsJoyMap);
populatePage(ui->tabHotkeysGeneral, hk_general_num, hk_general_labels, hkGeneralKeyMap, hkGeneralJoyMap);
int njoy = SDL_NumJoysticks();
if (njoy > 0)
{
for (int i = 0; i < njoy; i++)
{
const char* name = SDL_JoystickNameForIndex(i);
ui->cbxJoystick->addItem(QString(name));
}
ui->cbxJoystick->setCurrentIndex(Input::JoystickID);
}
else
{
ui->cbxJoystick->addItem("(no joysticks available)");
ui->cbxJoystick->setEnabled(false);
}
setupKeypadPage();
}
InputConfigDialog::~InputConfigDialog()
{
delete ui;
}
void InputConfigDialog::setupKeypadPage()
{
for (int i = 0; i < keypad_num; i++)
{
QPushButton* pushButtonKey = this->findChild<QPushButton*>(QStringLiteral("btnKey") + dskeylabels[i]);
QPushButton* pushButtonJoy = this->findChild<QPushButton*>(QStringLiteral("btnJoy") + dskeylabels[i]);
KeyMapButton* keyMapButtonKey = new KeyMapButton(&keypadKeyMap[i], false);
JoyMapButton* keyMapButtonJoy = new JoyMapButton(&keypadJoyMap[i], false);
pushButtonKey->parentWidget()->layout()->replaceWidget(pushButtonKey, keyMapButtonKey);
pushButtonJoy->parentWidget()->layout()->replaceWidget(pushButtonJoy, keyMapButtonJoy);
delete pushButtonKey;
delete pushButtonJoy;
if (ui->cbxJoystick->isEnabled())
{
ui->stackMapping->setCurrentIndex(1);
}
}
}
void InputConfigDialog::populatePage(QWidget* page, int num, const char** labels, int* keymap, int* joymap)
{
// kind of a hack
bool ishotkey = (page != ui->tabInput);
QHBoxLayout* main_layout = new QHBoxLayout();
QGroupBox* group;
QGridLayout* group_layout;
group = new QGroupBox("Keyboard mappings:");
main_layout->addWidget(group);
group_layout = new QGridLayout();
group_layout->setSpacing(1);
for (int i = 0; i < num; i++)
{
QLabel* label = new QLabel(QString(labels[i])+":");
KeyMapButton* btn = new KeyMapButton(&keymap[i], ishotkey);
group_layout->addWidget(label, i, 0);
group_layout->addWidget(btn, i, 1);
}
group_layout->setRowStretch(num, 1);
group->setLayout(group_layout);
group->setMinimumWidth(275);
group = new QGroupBox("Joystick mappings:");
main_layout->addWidget(group);
group_layout = new QGridLayout();
group_layout->setSpacing(1);
for (int i = 0; i < num; i++)
{
QLabel* label = new QLabel(QString(labels[i])+":");
JoyMapButton* btn = new JoyMapButton(&joymap[i], ishotkey);
group_layout->addWidget(label, i, 0);
group_layout->addWidget(btn, i, 1);
}
group_layout->setRowStretch(num, 1);
group->setLayout(group_layout);
group->setMinimumWidth(275);
page->setLayout(main_layout);
}
void InputConfigDialog::on_InputConfigDialog_accepted()
{
for (int i = 0; i < 12; i++)
{
Config::KeyMapping[dskeyorder[i]] = keypadKeyMap[i];
Config::JoyMapping[dskeyorder[i]] = keypadJoyMap[i];
}
for (int i = 0; i < 2; i++)
{
Config::HKKeyMapping[hk_addons[i]] = addonsKeyMap[i];
Config::HKJoyMapping[hk_addons[i]] = addonsJoyMap[i];
}
for (int i = 0; i < 9; i++)
{
Config::HKKeyMapping[hk_general[i]] = hkGeneralKeyMap[i];
Config::HKJoyMapping[hk_general[i]] = hkGeneralJoyMap[i];
}
Config::JoystickID = Input::JoystickID;
Config::Save();
closeDlg();
}
void InputConfigDialog::on_InputConfigDialog_rejected()
{
Input::JoystickID = Config::JoystickID;
Input::OpenJoystick();
closeDlg();
}
void InputConfigDialog::on_btnKeyMapSwitch_clicked()
{
ui->stackMapping->setCurrentIndex(0);
}
void InputConfigDialog::on_btnJoyMapSwitch_clicked()
{
ui->stackMapping->setCurrentIndex(1);
}
void InputConfigDialog::on_cbxJoystick_currentIndexChanged(int id)
{
// prevent a spurious change
if (ui->cbxJoystick->count() < 2) return;
Input::JoystickID = id;
Input::OpenJoystick();
}

View File

@ -55,10 +55,13 @@ private slots:
void on_InputConfigDialog_accepted(); void on_InputConfigDialog_accepted();
void on_InputConfigDialog_rejected(); void on_InputConfigDialog_rejected();
void on_btnKeyMapSwitch_clicked();
void on_btnJoyMapSwitch_clicked();
void on_cbxJoystick_currentIndexChanged(int id); void on_cbxJoystick_currentIndexChanged(int id);
private: private:
void populatePage(QWidget* page, int num, const char** labels, int* keymap, int* joymap); void populatePage(QWidget* page, int num, const char** labels, int* keymap, int* joymap);
void setupKeypadPage();
Ui::InputConfigDialog* ui; Ui::InputConfigDialog* ui;
@ -68,56 +71,4 @@ private:
}; };
class KeyMapButton : public QPushButton
{
Q_OBJECT
public:
explicit KeyMapButton(int* mapping, bool hotkey);
~KeyMapButton();
protected:
void keyPressEvent(QKeyEvent* event) override;
void focusOutEvent(QFocusEvent* event) override;
bool focusNextPrevChild(bool next) override { return false; }
private slots:
void onClick();
private:
QString mappingText();
int* mapping;
bool isHotkey;
};
class JoyMapButton : public QPushButton
{
Q_OBJECT
public:
explicit JoyMapButton(int* mapping, bool hotkey);
~JoyMapButton();
protected:
void keyPressEvent(QKeyEvent* event) override;
void focusOutEvent(QFocusEvent* event) override;
void timerEvent(QTimerEvent* event) override;
bool focusNextPrevChild(bool next) override { return false; }
private slots:
void onClick();
private:
QString mappingText();
int* mapping;
bool isHotkey;
int timerID;
int axesRest[16];
};
#endif // INPUTCONFIGDIALOG_H #endif // INPUTCONFIGDIALOG_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,355 @@
/*
Copyright 2016-2021 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef MAPBUTTON_H
#define MAPBUTTON_H
#include <QPushButton>
#include <SDL2/SDL.h>
#include "Input.h"
class KeyMapButton : public QPushButton
{
Q_OBJECT
public:
KeyMapButton(int* mapping, bool hotkey) : QPushButton()
{
this->mapping = mapping;
this->isHotkey = hotkey;
setCheckable(true);
setText(mappingText());
setFocusPolicy(Qt::StrongFocus); //Fixes binding keys in macOS
connect(this, &KeyMapButton::clicked, this, &KeyMapButton::onClick);
}
~KeyMapButton()
{
}
protected:
void keyPressEvent(QKeyEvent* event) override
{
if (!isChecked()) return QPushButton::keyPressEvent(event);
printf("KEY PRESSED = %08X %08X | %08X %08X %08X\n", event->key(), (int)event->modifiers(), event->nativeVirtualKey(), event->nativeModifiers(), event->nativeScanCode());
int key = event->key();
int mod = event->modifiers();
bool ismod = (key == Qt::Key_Control ||
key == Qt::Key_Alt ||
key == Qt::Key_AltGr ||
key == Qt::Key_Shift ||
key == Qt::Key_Meta);
if (!mod)
{
if (key == Qt::Key_Escape) { click(); return; }
if (key == Qt::Key_Backspace) { *mapping = -1; click(); return; }
}
if (isHotkey)
{
if (ismod)
return;
}
if (!ismod)
key |= mod;
else if (Input::IsRightModKey(event))
key |= (1<<31);
*mapping = key;
click();
}
void focusOutEvent(QFocusEvent* event) override
{
if (isChecked())
{
// if we lost the focus while mapping, consider it 'done'
click();
}
QPushButton::focusOutEvent(event);
}
bool focusNextPrevChild(bool next) override { return false; }
private slots:
void onClick()
{
if (isChecked())
{
setText("[press key]");
}
else
{
setText(mappingText());
}
}
private:
QString mappingText()
{
int key = *mapping;
if (key == -1) return "None";
QString isright = (key & (1<<31)) ? "Right " : "Left ";
key &= ~(1<<31);
#ifndef __APPLE__
switch (key)
{
case Qt::Key_Control: return isright + "Ctrl";
case Qt::Key_Alt: return "Alt";
case Qt::Key_AltGr: return "AltGr";
case Qt::Key_Shift: return isright + "Shift";
case Qt::Key_Meta: return "Meta";
}
#else
switch (key)
{
case Qt::Key_Control: return isright + "";
case Qt::Key_Alt: return isright + "";
case Qt::Key_Shift: return isright + "";
case Qt::Key_Meta: return isright + "";
}
#endif
QKeySequence seq(key);
QString ret = seq.toString(QKeySequence::NativeText);
// weak attempt at detecting garbage key names
//if (ret.length() == 2 && ret[0].unicode() > 0xFF)
// return QString("[%1]").arg(key, 8, 16);
return ret.replace("&", "&&");
}
int* mapping;
bool isHotkey;
};
class JoyMapButton : public QPushButton
{
Q_OBJECT
public:
JoyMapButton(int* mapping, bool hotkey) : QPushButton()
{
this->mapping = mapping;
this->isHotkey = hotkey;
setCheckable(true);
setText(mappingText());
connect(this, &JoyMapButton::clicked, this, &JoyMapButton::onClick);
timerID = 0;
}
~JoyMapButton()
{
}
protected:
void keyPressEvent(QKeyEvent* event) override
{
if (!isChecked()) return QPushButton::keyPressEvent(event);
int key = event->key();
int mod = event->modifiers();
if (!mod)
{
if (key == Qt::Key_Escape) { click(); return; }
if (key == Qt::Key_Backspace) { *mapping = -1; click(); return; }
}
}
void focusOutEvent(QFocusEvent* event) override
{
if (isChecked())
{
// if we lost the focus while mapping, consider it 'done'
click();
}
QPushButton::focusOutEvent(event);
}
void timerEvent(QTimerEvent* event) override
{
SDL_Joystick* joy = Input::Joystick;
if (!joy) { click(); return; }
if (!SDL_JoystickGetAttached(joy)) { click(); return; }
int oldmap;
if (*mapping == -1) oldmap = 0xFFFF;
else oldmap = *mapping;
int nbuttons = SDL_JoystickNumButtons(joy);
for (int i = 0; i < nbuttons; i++)
{
if (SDL_JoystickGetButton(joy, i))
{
*mapping = (oldmap & 0xFFFF0000) | i;
click();
return;
}
}
int nhats = SDL_JoystickNumHats(joy);
if (nhats > 16) nhats = 16;
for (int i = 0; i < nhats; i++)
{
Uint8 blackhat = SDL_JoystickGetHat(joy, i);
if (blackhat)
{
if (blackhat & 0x1) blackhat = 0x1;
else if (blackhat & 0x2) blackhat = 0x2;
else if (blackhat & 0x4) blackhat = 0x4;
else blackhat = 0x8;
*mapping = (oldmap & 0xFFFF0000) | 0x100 | blackhat | (i << 4);
click();
return;
}
}
int naxes = SDL_JoystickNumAxes(joy);
if (naxes > 16) naxes = 16;
for (int i = 0; i < naxes; i++)
{
Sint16 axisval = SDL_JoystickGetAxis(joy, i);
int diff = abs(axisval - axesRest[i]);
if (axesRest[i] < -16384 && axisval >= 0)
{
*mapping = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
click();
return;
}
else if (diff > 16384)
{
int axistype;
if (axisval > 0) axistype = 0;
else axistype = 1;
*mapping = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
click();
return;
}
}
}
bool focusNextPrevChild(bool next) override { return false; }
private slots:
void onClick()
{
if (isChecked())
{
setText("[press button/axis]");
timerID = startTimer(50);
memset(axesRest, 0, sizeof(axesRest));
if (Input::Joystick && SDL_JoystickGetAttached(Input::Joystick))
{
int naxes = SDL_JoystickNumAxes(Input::Joystick);
if (naxes > 16) naxes = 16;
for (int a = 0; a < naxes; a++)
{
axesRest[a] = SDL_JoystickGetAxis(Input::Joystick, a);
}
}
}
else
{
setText(mappingText());
if (timerID) { killTimer(timerID); timerID = 0; }
}
}
private:
QString mappingText()
{
int id = *mapping;
if (id == -1) return "None";
bool hasbtn = ((id & 0xFFFF) != 0xFFFF);
QString str;
if (hasbtn)
{
if (id & 0x100)
{
int hatnum = ((id >> 4) & 0xF) + 1;
switch (id & 0xF)
{
case 0x1: str = "Hat %1 up"; break;
case 0x2: str = "Hat %1 right"; break;
case 0x4: str = "Hat %1 down"; break;
case 0x8: str = "Hat %1 left"; break;
}
str = str.arg(hatnum);
}
else
{
str = QString("Button %1").arg((id & 0xFFFF) + 1);
}
}
else
{
str = "";
}
if (id & 0x10000)
{
int axisnum = ((id >> 24) & 0xF) + 1;
if (hasbtn) str += " / ";
switch ((id >> 20) & 0xF)
{
case 0: str += QString("Axis %1 +").arg(axisnum); break;
case 1: str += QString("Axis %1 -").arg(axisnum); break;
case 2: str += QString("Trigger %1").arg(axisnum); break;
}
}
return str;
}
int* mapping;
bool isHotkey;
int timerID;
int axesRest[16];
};
#endif // MAPBUTTON_H

View File

@ -0,0 +1,6 @@
These vector images are modified from the [Nintendo DS Lite illustration on dimensions.com](https://www.dimensions.com/element/nintendo-ds-lite).
These have been used with the permission of the copyright holders.
> "We restrict the usage of our drawings and 3D models in commercial software, but as long as it's a free and open source community project, that would be approved. Any reference/backlink to Dimensions.com that could be provided in the developer notes and/or credits for the project would be sufficient for use."
https://www.dimensions.com/legal

View File

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="ds">
<file>ds_open.svg</file>
<file>ds_back.svg</file>
</qresource>
</RCC>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -1,511 +0,0 @@
/*
Copyright 2016-2021 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <QGroupBox>
#include <QLabel>
#include <QKeyEvent>
#include <SDL2/SDL.h>
#include "types.h"
#include "Config.h"
#include "PlatformConfig.h"
#include "Input.h"
#include "InputConfigDialog.h"
#include "ui_InputConfigDialog.h"
InputConfigDialog* InputConfigDialog::currentDlg = nullptr;
const int dskeyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 2, 3};
const char* dskeylabels[12] = {"A", "B", "X", "Y", "Left", "Right", "Up", "Down", "L", "R", "Select", "Start"};
const int hk_addons[] =
{
HK_SolarSensorIncrease,
HK_SolarSensorDecrease,
};
const char* hk_addons_labels[] =
{
"[Boktai] Sunlight + ",
"[Boktai] Sunlight - ",
};
const int hk_general[] =
{
HK_Pause,
HK_Reset,
HK_FrameStep,
HK_FastForward,
HK_FastForwardToggle,
HK_FullscreenToggle,
HK_Lid,
HK_Mic,
HK_SwapScreens
};
const char* hk_general_labels[] =
{
"Pause/resume",
"Reset",
"Frame step",
"Fast forward",
"Toggle FPS limit",
"Toggle Fullscreen",
"Close/open lid",
"Microphone",
"Swap screens"
};
InputConfigDialog::InputConfigDialog(QWidget* parent) : QDialog(parent), ui(new Ui::InputConfigDialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
for (int i = 0; i < 12; i++)
{
keypadKeyMap[i] = Config::KeyMapping[dskeyorder[i]];
keypadJoyMap[i] = Config::JoyMapping[dskeyorder[i]];
}
for (int i = 0; i < 2; i++)
{
addonsKeyMap[i] = Config::HKKeyMapping[hk_addons[i]];
addonsJoyMap[i] = Config::HKJoyMapping[hk_addons[i]];
}
for (int i = 0; i < 9; i++)
{
hkGeneralKeyMap[i] = Config::HKKeyMapping[hk_general[i]];
hkGeneralJoyMap[i] = Config::HKJoyMapping[hk_general[i]];
}
populatePage(ui->tabInput, 12, dskeylabels, keypadKeyMap, keypadJoyMap);
populatePage(ui->tabAddons, 2, hk_addons_labels, addonsKeyMap, addonsJoyMap);
populatePage(ui->tabHotkeysGeneral, 9, hk_general_labels, hkGeneralKeyMap, hkGeneralJoyMap);
int njoy = SDL_NumJoysticks();
if (njoy > 0)
{
for (int i = 0; i < njoy; i++)
{
const char* name = SDL_JoystickNameForIndex(i);
ui->cbxJoystick->addItem(QString(name));
}
ui->cbxJoystick->setCurrentIndex(Input::JoystickID);
}
else
{
ui->cbxJoystick->addItem("(no joysticks available)");
ui->cbxJoystick->setEnabled(false);
}
}
InputConfigDialog::~InputConfigDialog()
{
delete ui;
}
void InputConfigDialog::populatePage(QWidget* page, int num, const char** labels, int* keymap, int* joymap)
{
// kind of a hack
bool ishotkey = (page != ui->tabInput);
QHBoxLayout* main_layout = new QHBoxLayout();
QGroupBox* group;
QGridLayout* group_layout;
group = new QGroupBox("Keyboard mappings:");
main_layout->addWidget(group);
group_layout = new QGridLayout();
group_layout->setSpacing(1);
for (int i = 0; i < num; i++)
{
QLabel* label = new QLabel(QString(labels[i])+":");
KeyMapButton* btn = new KeyMapButton(&keymap[i], ishotkey);
group_layout->addWidget(label, i, 0);
group_layout->addWidget(btn, i, 1);
}
group_layout->setRowStretch(num, 1);
group->setLayout(group_layout);
group->setMinimumWidth(275);
group = new QGroupBox("Joystick mappings:");
main_layout->addWidget(group);
group_layout = new QGridLayout();
group_layout->setSpacing(1);
for (int i = 0; i < num; i++)
{
QLabel* label = new QLabel(QString(labels[i])+":");
JoyMapButton* btn = new JoyMapButton(&joymap[i], ishotkey);
group_layout->addWidget(label, i, 0);
group_layout->addWidget(btn, i, 1);
}
group_layout->setRowStretch(num, 1);
group->setLayout(group_layout);
group->setMinimumWidth(275);
page->setLayout(main_layout);
}
void InputConfigDialog::on_InputConfigDialog_accepted()
{
for (int i = 0; i < 12; i++)
{
Config::KeyMapping[dskeyorder[i]] = keypadKeyMap[i];
Config::JoyMapping[dskeyorder[i]] = keypadJoyMap[i];
}
for (int i = 0; i < 2; i++)
{
Config::HKKeyMapping[hk_addons[i]] = addonsKeyMap[i];
Config::HKJoyMapping[hk_addons[i]] = addonsJoyMap[i];
}
for (int i = 0; i < 9; i++)
{
Config::HKKeyMapping[hk_general[i]] = hkGeneralKeyMap[i];
Config::HKJoyMapping[hk_general[i]] = hkGeneralJoyMap[i];
}
Config::JoystickID = Input::JoystickID;
Config::Save();
closeDlg();
}
void InputConfigDialog::on_InputConfigDialog_rejected()
{
Input::JoystickID = Config::JoystickID;
Input::OpenJoystick();
closeDlg();
}
void InputConfigDialog::on_cbxJoystick_currentIndexChanged(int id)
{
// prevent a spurious change
if (ui->cbxJoystick->count() < 2) return;
Input::JoystickID = id;
Input::OpenJoystick();
}
KeyMapButton::KeyMapButton(int* mapping, bool hotkey) : QPushButton()
{
this->mapping = mapping;
this->isHotkey = hotkey;
setCheckable(true);
setText(mappingText());
setFocusPolicy(Qt::StrongFocus); //Fixes binding keys in macOS
connect(this, &KeyMapButton::clicked, this, &KeyMapButton::onClick);
}
KeyMapButton::~KeyMapButton()
{
}
void KeyMapButton::keyPressEvent(QKeyEvent* event)
{
if (!isChecked()) return QPushButton::keyPressEvent(event);
printf("KEY PRESSED = %08X %08X | %08X %08X %08X\n", event->key(), (int)event->modifiers(), event->nativeVirtualKey(), event->nativeModifiers(), event->nativeScanCode());
int key = event->key();
int mod = event->modifiers();
bool ismod = (key == Qt::Key_Control ||
key == Qt::Key_Alt ||
key == Qt::Key_AltGr ||
key == Qt::Key_Shift ||
key == Qt::Key_Meta);
if (!mod)
{
if (key == Qt::Key_Escape) { click(); return; }
if (key == Qt::Key_Backspace) { *mapping = -1; click(); return; }
}
if (isHotkey)
{
if (ismod)
return;
}
if (!ismod)
key |= mod;
else if (Input::IsRightModKey(event))
key |= (1<<31);
*mapping = key;
click();
}
void KeyMapButton::focusOutEvent(QFocusEvent* event)
{
if (isChecked())
{
// if we lost the focus while mapping, consider it 'done'
click();
}
QPushButton::focusOutEvent(event);
}
void KeyMapButton::onClick()
{
if (isChecked())
{
setText("[press key]");
}
else
{
setText(mappingText());
}
}
QString KeyMapButton::mappingText()
{
int key = *mapping;
if (key == -1) return "None";
QString isright = (key & (1<<31)) ? "Right " : "Left ";
key &= ~(1<<31);
#ifndef __APPLE__
switch (key)
{
case Qt::Key_Control: return isright + "Ctrl";
case Qt::Key_Alt: return "Alt";
case Qt::Key_AltGr: return "AltGr";
case Qt::Key_Shift: return isright + "Shift";
case Qt::Key_Meta: return "Meta";
}
#else
switch (key)
{
case Qt::Key_Control: return isright + "";
case Qt::Key_Alt: return isright + "";
case Qt::Key_Shift: return isright + "";
case Qt::Key_Meta: return isright + "";
}
#endif
QKeySequence seq(key);
QString ret = seq.toString(QKeySequence::NativeText);
// weak attempt at detecting garbage key names
//if (ret.length() == 2 && ret[0].unicode() > 0xFF)
// return QString("[%1]").arg(key, 8, 16);
return ret.replace("&", "&&");
}
JoyMapButton::JoyMapButton(int* mapping, bool hotkey) : QPushButton()
{
this->mapping = mapping;
this->isHotkey = hotkey;
setCheckable(true);
setText(mappingText());
connect(this, &JoyMapButton::clicked, this, &JoyMapButton::onClick);
timerID = 0;
}
JoyMapButton::~JoyMapButton()
{
}
void JoyMapButton::keyPressEvent(QKeyEvent* event)
{
if (!isChecked()) return QPushButton::keyPressEvent(event);
int key = event->key();
int mod = event->modifiers();
if (!mod)
{
if (key == Qt::Key_Escape) { click(); return; }
if (key == Qt::Key_Backspace) { *mapping = -1; click(); return; }
}
}
void JoyMapButton::focusOutEvent(QFocusEvent* event)
{
if (isChecked())
{
// if we lost the focus while mapping, consider it 'done'
click();
}
QPushButton::focusOutEvent(event);
}
void JoyMapButton::timerEvent(QTimerEvent* event)
{
SDL_Joystick* joy = Input::Joystick;
if (!joy) { click(); return; }
if (!SDL_JoystickGetAttached(joy)) { click(); return; }
int oldmap;
if (*mapping == -1) oldmap = 0xFFFF;
else oldmap = *mapping;
int nbuttons = SDL_JoystickNumButtons(joy);
for (int i = 0; i < nbuttons; i++)
{
if (SDL_JoystickGetButton(joy, i))
{
*mapping = (oldmap & 0xFFFF0000) | i;
click();
return;
}
}
int nhats = SDL_JoystickNumHats(joy);
if (nhats > 16) nhats = 16;
for (int i = 0; i < nhats; i++)
{
Uint8 blackhat = SDL_JoystickGetHat(joy, i);
if (blackhat)
{
if (blackhat & 0x1) blackhat = 0x1;
else if (blackhat & 0x2) blackhat = 0x2;
else if (blackhat & 0x4) blackhat = 0x4;
else blackhat = 0x8;
*mapping = (oldmap & 0xFFFF0000) | 0x100 | blackhat | (i << 4);
click();
return;
}
}
int naxes = SDL_JoystickNumAxes(joy);
if (naxes > 16) naxes = 16;
for (int i = 0; i < naxes; i++)
{
Sint16 axisval = SDL_JoystickGetAxis(joy, i);
int diff = abs(axisval - axesRest[i]);
if (axesRest[i] < -16384 && axisval >= 0)
{
*mapping = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
click();
return;
}
else if (diff > 16384)
{
int axistype;
if (axisval > 0) axistype = 0;
else axistype = 1;
*mapping = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
click();
return;
}
}
}
void JoyMapButton::onClick()
{
if (isChecked())
{
setText("[press button/axis]");
timerID = startTimer(50);
memset(axesRest, 0, sizeof(axesRest));
if (Input::Joystick && SDL_JoystickGetAttached(Input::Joystick))
{
int naxes = SDL_JoystickNumAxes(Input::Joystick);
if (naxes > 16) naxes = 16;
for (int a = 0; a < naxes; a++)
{
axesRest[a] = SDL_JoystickGetAxis(Input::Joystick, a);
}
}
}
else
{
setText(mappingText());
if (timerID) { killTimer(timerID); timerID = 0; }
}
}
QString JoyMapButton::mappingText()
{
int id = *mapping;
if (id == -1) return "None";
bool hasbtn = ((id & 0xFFFF) != 0xFFFF);
QString str;
if (hasbtn)
{
if (id & 0x100)
{
int hatnum = ((id >> 4) & 0xF) + 1;
switch (id & 0xF)
{
case 0x1: str = "Hat %1 up"; break;
case 0x2: str = "Hat %1 right"; break;
case 0x4: str = "Hat %1 down"; break;
case 0x8: str = "Hat %1 left"; break;
}
str = str.arg(hatnum);
}
else
{
str = QString("Button %1").arg((id & 0xFFFF) + 1);
}
}
else
{
str = "";
}
if (id & 0x10000)
{
int axisnum = ((id >> 24) & 0xF) + 1;
if (hasbtn) str += " / ";
switch ((id >> 20) & 0xF)
{
case 0: str += QString("Axis %1 +").arg(axisnum); break;
case 1: str += QString("Axis %1 -").arg(axisnum); break;
case 2: str += QString("Trigger %1").arg(axisnum); break;
}
}
return str;
}

View File

@ -1,131 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InputConfigDialog</class>
<widget class="QDialog" name="InputConfigDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>488</width>
<height>365</height>
</rect>
</property>
<property name="windowTitle">
<string>Input and hotkeys - melonDS</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabInput">
<attribute name="title">
<string>DS keypad</string>
</attribute>
</widget>
<widget class="QWidget" name="tabAddons">
<attribute name="title">
<string>Add-ons</string>
</attribute>
</widget>
<widget class="QWidget" name="tabHotkeysGeneral">
<attribute name="title">
<string>General hotkeys</string>
</attribute>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Joystick:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbxJoystick">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects which joystick will be used for joystick input, if any is present.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>InputConfigDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>InputConfigDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -52,7 +52,7 @@
#include "Input.h" #include "Input.h"
#include "CheatsDialog.h" #include "CheatsDialog.h"
#include "EmuSettingsDialog.h" #include "EmuSettingsDialog.h"
#include "InputConfigDialog.h" #include "InputConfig/InputConfigDialog.h"
#include "VideoSettingsDialog.h" #include "VideoSettingsDialog.h"
#include "AudioSettingsDialog.h" #include "AudioSettingsDialog.h"
#include "WifiSettingsDialog.h" #include "WifiSettingsDialog.h"

View File

@ -203,7 +203,7 @@ fixup_libs(executable, executable)
bundle_plugins = File.join($bundle, "Contents", "PlugIns") bundle_plugins = File.join($bundle, "Contents", "PlugIns")
want_plugins = ["styles/libqmacstyle.dylib", "platforms/libqcocoa.dylib"] want_plugins = ["styles/libqmacstyle.dylib", "platforms/libqcocoa.dylib", "imageformats/libqsvg.dylib"]
want_plugins.each do |plug| want_plugins.each do |plug|
destdir = File.join(bundle_plugins, File.dirname(plug)) destdir = File.join(bundle_plugins, File.dirname(plug))
FileUtils.mkdir_p(destdir) FileUtils.mkdir_p(destdir)