citra-qt: Add input settings configuration

This commit is contained in:
Kloen 2015-09-07 16:37:13 +02:00
parent 09f43c0975
commit 77a68e0607
10 changed files with 263 additions and 16 deletions

View File

@ -41,12 +41,12 @@ bool Config::LoadINI(INIReader* config, const char* location, const std::string&
} }
static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = { static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
GLFW_KEY_A, GLFW_KEY_S, GLFW_KEY_Z, GLFW_KEY_X, GLFW_KEY_X, GLFW_KEY_Z, GLFW_KEY_S, GLFW_KEY_A,
GLFW_KEY_Q, GLFW_KEY_W, GLFW_KEY_1, GLFW_KEY_2, GLFW_KEY_Q, GLFW_KEY_W, GLFW_KEY_1, GLFW_KEY_2,
GLFW_KEY_M, GLFW_KEY_N, GLFW_KEY_B, GLFW_KEY_I, GLFW_KEY_K, GLFW_KEY_J, GLFW_KEY_L,
GLFW_KEY_T, GLFW_KEY_G, GLFW_KEY_F, GLFW_KEY_H, GLFW_KEY_T, GLFW_KEY_G, GLFW_KEY_F, GLFW_KEY_H,
GLFW_KEY_UP, GLFW_KEY_DOWN, GLFW_KEY_LEFT, GLFW_KEY_RIGHT, GLFW_KEY_UP, GLFW_KEY_DOWN, GLFW_KEY_LEFT, GLFW_KEY_RIGHT,
GLFW_KEY_I, GLFW_KEY_K, GLFW_KEY_J, GLFW_KEY_L GLFW_KEY_M, GLFW_KEY_N, GLFW_KEY_B
}; };
void Config::ReadValues() { void Config::ReadValues() {

View File

@ -4,6 +4,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(SRCS set(SRCS
config/controller_config.cpp config/controller_config.cpp
config/controller_config_util.cpp config/controller_config_util.cpp
config/inputs.cpp
config.cpp config.cpp
debugger/callstack.cpp debugger/callstack.cpp
debugger/disassembler.cpp debugger/disassembler.cpp
@ -28,6 +29,7 @@ set(SRCS
set(HEADERS set(HEADERS
config/controller_config.h config/controller_config.h
config/controller_config_util.h config/controller_config_util.h
config/inputs.h
config.h config.h
debugger/callstack.h debugger/callstack.h
debugger/disassembler.h debugger/disassembler.h

View File

@ -21,13 +21,13 @@ Config::Config() {
Reload(); Reload();
} }
static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults = { const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = {
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_X, Qt::Key_Z, Qt::Key_S, Qt::Key_A,
Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2,
Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L,
Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right,
Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L Qt::Key_M, Qt::Key_N, Qt::Key_B
}; };
void Config::ReadValues() { void Config::ReadValues() {

View File

@ -4,8 +4,11 @@
#pragma once #pragma once
#include <array>
#include <string> #include <string>
#include "core/settings.h"
class QSettings; class QSettings;
class Config { class Config {
@ -20,4 +23,5 @@ public:
void Reload(); void Reload();
void Save(); void Save();
static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults;
}; };

View File

@ -0,0 +1,170 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QKeySequence>
#include <QLabel>
#include <QSettings>
#include <QVBoxLayout>
#include "citra_qt/config.h"
#include "citra_qt/config/inputs.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
InputsDialog::InputsDialog(QWidget* parent) : QDialog(parent) {
// create a copy of the current settings
temp_settings = Settings::Values(Settings::values);
signal_mapper = new QSignalMapper(this);
// create the QPushButtons
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
push_buttons[i] = new QPushButton(this);
push_buttons[i]->setCheckable(true);
push_buttons[i]->setDefault(false);
push_buttons[i]->setAutoDefault(false);
}
static const std::string label_array[] = {
"Y:", "X:", "B:", "A:",
"L:", "R:", "ZL:", "ZR:",
"Left:", "Right:", "Up:", "Down:",
"Left:", "Right:", "Up:", "Down:",
"Left:", "Right:", "Up:", "Down:",
"Start:", "Select:", "Home:"
};
static const std::string group_names[] = {
"Face Buttons", "Shoulder Buttons", "C-Stick",
"Directional Pad", "Circle Pad", "System Buttons"
};
QGridLayout* grid = new QGridLayout();
int id = 0;
for (int i = 0; i < ARRAY_SIZE(group_names); ++i) {
QGroupBox* group = new QGroupBox(tr(group_names[i].c_str()));
QGridLayout* box = new QGridLayout;
for (int x = 0; x <= (Settings::NativeInput::NUM_INPUTS / ARRAY_SIZE(group_names)); ++x) {
QLabel* label = new QLabel(tr(label_array[id].c_str()));
box->addWidget(label, (int) x / 2 * 2, (int) x % 2 * 2);
box->addWidget(push_buttons[id], (int) x / 2 * 2 + 1, (int) x % 2 * 2);
signal_mapper->setMapping(push_buttons[id], id);
connect(push_buttons[id], SIGNAL(clicked()), signal_mapper, SLOT(map()));
++id;
if (id >= Settings::NativeInput::NUM_INPUTS)
break;
}
group->setLayout(box);
grid->addWidget(group, (int) i % 3, (int) i / 3 * 2);
}
connect(signal_mapper, SIGNAL(mapped(int)), this, SLOT(ChangeValue(int)));
// the button box that contains the restore default/ok/cancel buttons
QVBoxLayout* verticalLayout = new QVBoxLayout();
QDialogButtonBox* buttonBox = new QDialogButtonBox();
buttonBox->setOrientation(Qt::Horizontal);
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok | QDialogButtonBox::RestoreDefaults);
verticalLayout->addLayout(grid);
verticalLayout->addWidget(buttonBox);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(SaveSettings()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
setLayout(verticalLayout);
setWindowTitle(tr("Input Settings"));
// set up event handlers for the buttons
QPushButton* defaultButton = buttonBox->button(QDialogButtonBox::RestoreDefaults);
connect(defaultButton, SIGNAL(clicked()), this, SLOT(RestoreDefaultSettings()));
// display current key settings
UpdateKeyLabels();
}
void InputsDialog::ChangeValue(int id) {
if (current_button_id > -1)
push_buttons[current_button_id]->setChecked(false);
if (current_button_id == id) {
push_buttons[current_button_id]->setChecked(false);
current_button_id = -1;
} else {
push_buttons[id]->setChecked(true);
current_button_id = id;
}
}
void InputsDialog::UpdateValue(int key) {
bool update = std::none_of(std::begin(Settings::NativeInput::All), std::end(Settings::NativeInput::All),
[&](Settings::NativeInput::Values val) { return key == temp_settings.input_mappings[val]; });
if (update) {
temp_settings.input_mappings[Settings::NativeInput::All[current_button_id]] = key;
push_buttons[current_button_id]->setChecked(false);
push_buttons[current_button_id]->setText(GetKeyName(temp_settings.input_mappings[Settings::NativeInput::All[current_button_id]]));
current_button_id = -1;
}
}
void InputsDialog::UpdateKeyLabels() {
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
push_buttons[i]->setText(GetKeyName(temp_settings.input_mappings[Settings::NativeInput::All[i]]));
}
}
QString InputsDialog::GetKeyName(int key_code) {
if (key_code == Qt::Key_Shift)
return tr("Shift");
if (key_code == Qt::Key_Control)
return tr("Ctrl");
if (key_code == Qt::Key_Alt)
return tr("Alt");
if (key_code == Qt::Key_Meta)
return tr("Meta");
return QKeySequence(key_code).toString();
}
void InputsDialog::RestoreDefaultSettings() {
// load the default button settings into temp_settings
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
temp_settings.input_mappings[Settings::NativeInput::All[i]] = Config::defaults[i].toInt();
}
// then display it
UpdateKeyLabels();
}
void InputsDialog::SaveSettings() {
Config config;
// load the temporary settings into our real settings
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
Settings::values.input_mappings[Settings::NativeInput::All[i]] =
temp_settings.input_mappings[Settings::NativeInput::All[i]];
}
// then save it
config.Save();
// Close the dialog after save the bindings
accept();
}
void InputsDialog::keyPressEvent(QKeyEvent* key) {
if (current_button_id != -1 && key->key() != Qt::Key_Escape)
UpdateValue(key->key());
else
QDialog::keyPressEvent(key);
}

View File

@ -0,0 +1,56 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <QDialog>
#include <QKeyEvent>
#include <QPushButton>
#include <QSignalMapper>
#include "core/settings.h"
/// The input configuration dialog
class InputsDialog : public QDialog {
Q_OBJECT
public:
InputsDialog(QWidget* parent = nullptr);
/**
* Given a key code, return the key name.
* @note This function is conceptually similar to "QKeySequence(key_code).toString()", which however returns gibberish strings for modifier keys (e.g. Ctrl). Hence this function provides special cases for these keys.
*/
static QString GetKeyName(int key_code);
void keyPressEvent(QKeyEvent* event) override;
private:
/// Update QPushButton labels to reflect current button mapping.
void UpdateKeyLabels();
/// An array of all the QPushButtons for easy iteration over them
std::array<QPushButton*, 23> push_buttons;
/// Temporary settings used when configuration is changed but not saved yet
Settings::Values temp_settings;
/// Map of all the QPushButtons signals
QSignalMapper* signal_mapper;
/// Current QPushButton id, it's -1 as default value since it's an incorrect id, right ones are from 0 to 22.
int current_button_id = -1;
private slots:
void SaveSettings();
void RestoreDefaultSettings();
/// Set/Unset the QPushButton with the given id to allow it to modify his bind key. Discards previous selected button.
void ChangeValue(int id);
/// Update the QPushButton & key binding of the current_button_id with the given key if it's possible (Unused and allowed key).
void UpdateValue(int key);
};

View File

@ -15,6 +15,8 @@
#include "citra_qt/hotkeys.h" #include "citra_qt/hotkeys.h"
#include "citra_qt/main.h" #include "citra_qt/main.h"
#include "citra_qt/config/inputs.h"
// Debugger // Debugger
#include "citra_qt/debugger/callstack.h" #include "citra_qt/debugger/callstack.h"
#include "citra_qt/debugger/disassembler.h" #include "citra_qt/debugger/disassembler.h"
@ -169,6 +171,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
connect(ui.action_Use_Shader_JIT, SIGNAL(triggered(bool)), this, SLOT(SetShaderJITEnabled(bool))); connect(ui.action_Use_Shader_JIT, SIGNAL(triggered(bool)), this, SLOT(SetShaderJITEnabled(bool)));
connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog())); connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog()));
connect(ui.action_Inputs, SIGNAL(triggered()), this, SLOT(OnOpenInputsDialog()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping()));
@ -418,6 +421,11 @@ void GMainWindow::SetShaderJITEnabled(bool enabled) {
VideoCore::g_shader_jit_enabled = enabled; VideoCore::g_shader_jit_enabled = enabled;
} }
void GMainWindow::OnOpenInputsDialog() {
InputsDialog dialog(this);
dialog.exec();
}
void GMainWindow::ToggleWindowMode() { void GMainWindow::ToggleWindowMode() {
if (ui.action_Single_Window_Mode->isChecked()) { if (ui.action_Single_Window_Mode->isChecked()) {
// Render in the main window... // Render in the main window...

View File

@ -95,6 +95,7 @@ private slots:
void OnDisplayTitleBars(bool); void OnDisplayTitleBars(bool);
void SetHardwareRendererEnabled(bool); void SetHardwareRendererEnabled(bool);
void SetShaderJITEnabled(bool); void SetShaderJITEnabled(bool);
void OnOpenInputsDialog();
void ToggleWindowMode(); void ToggleWindowMode();
private: private:

View File

@ -75,6 +75,7 @@
<addaction name="action_Use_Hardware_Renderer"/> <addaction name="action_Use_Hardware_Renderer"/>
<addaction name="action_Use_Shader_JIT"/> <addaction name="action_Use_Shader_JIT"/>
<addaction name="action_Configure"/> <addaction name="action_Configure"/>
<addaction name="action_Inputs"/>
</widget> </widget>
<widget class="QMenu" name="menu_View"> <widget class="QMenu" name="menu_View">
<property name="title"> <property name="title">
@ -153,6 +154,11 @@
<string>Configure &amp;Hotkeys ...</string> <string>Configure &amp;Hotkeys ...</string>
</property> </property>
</action> </action>
<action name="action_Inputs">
<property name="text">
<string>Configure &amp;Controls</string>
</property>
</action>
<action name="action_Use_Hardware_Renderer"> <action name="action_Use_Hardware_Renderer">
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>

View File

@ -11,29 +11,29 @@ namespace Settings {
namespace NativeInput { namespace NativeInput {
enum Values { enum Values {
A, B, X, Y, Y, X, B, A,
L, R, ZL, ZR, L, R, ZL, ZR,
START, SELECT, HOME, CUP, CDOWN, CLEFT, CRIGHT,
DUP, DDOWN, DLEFT, DRIGHT, DUP, DDOWN, DLEFT, DRIGHT,
SUP, SDOWN, SLEFT, SRIGHT, SUP, SDOWN, SLEFT, SRIGHT,
CUP, CDOWN, CLEFT, CRIGHT, START, SELECT, HOME,
NUM_INPUTS NUM_INPUTS
}; };
static const std::array<const char*, NUM_INPUTS> Mapping = {{ static const std::array<const char*, NUM_INPUTS> Mapping = {{
"pad_a", "pad_b", "pad_x", "pad_y", "pad_y", "pad_x", "pad_b", "pad_a",
"pad_l", "pad_r", "pad_zl", "pad_zr", "pad_l", "pad_r", "pad_zl", "pad_zr",
"pad_start", "pad_select", "pad_home", "pad_cup", "pad_cdown", "pad_cleft", "pad_cright",
"pad_dup", "pad_ddown", "pad_dleft", "pad_dright", "pad_dup", "pad_ddown", "pad_dleft", "pad_dright",
"pad_sup", "pad_sdown", "pad_sleft", "pad_sright", "pad_sup", "pad_sdown", "pad_sleft", "pad_sright",
"pad_cup", "pad_cdown", "pad_cleft", "pad_cright" "pad_start", "pad_select", "pad_home"
}}; }};
static const std::array<Values, NUM_INPUTS> All = {{ static const std::array<Values, NUM_INPUTS> All = {{
A, B, X, Y, Y, X, B, A,
L, R, ZL, ZR, L, R, ZL, ZR,
START, SELECT, HOME, CUP, CDOWN, CLEFT, CRIGHT,
DUP, DDOWN, DLEFT, DRIGHT, DUP, DDOWN, DLEFT, DRIGHT,
SUP, SDOWN, SLEFT, SRIGHT, SUP, SDOWN, SLEFT, SRIGHT,
CUP, CDOWN, CLEFT, CRIGHT START, SELECT, HOME
}}; }};
} }