Merge commit 'bf8fde59c62cd6cec4a6d37e25f226bf5d40173e' into medusa

This commit is contained in:
Vicki Pfau 2019-09-28 15:00:00 -07:00
commit b46cdaa246
18 changed files with 1515 additions and 737 deletions

106
src/platform/qt/Action.cpp Normal file
View File

@ -0,0 +1,106 @@
/* Copyright (c) 2013-2018 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Action.h"
using namespace QGBA;
Action::Action(QObject* parent)
: QObject(parent)
{
}
Action::Action(Function function, const QString& name, const QString& visibleName, QObject* parent)
: QObject(parent)
, m_function(function)
, m_name(name)
, m_visibleName(visibleName)
{
}
Action::Action(Action::BooleanFunction function, const QString& name, const QString& visibleName, QObject* parent)
: QObject(parent)
, m_booleanFunction(function)
, m_name(name)
, m_visibleName(visibleName)
{
}
Action::Action(const QString& name, const QString& visibleName, QObject* parent)
: QObject(parent)
, m_name(name)
, m_visibleName(visibleName)
{
}
Action::Action(const Action& other)
: QObject(other.parent())
, m_enabled(other.m_enabled)
, m_active(other.m_active)
, m_function(other.m_function)
, m_booleanFunction(other.m_booleanFunction)
, m_name(other.m_name)
, m_visibleName(other.m_visibleName)
{
}
Action::Action(Action& other)
: QObject(other.parent())
, m_enabled(other.m_enabled)
, m_active(other.m_active)
, m_function(other.m_function)
, m_booleanFunction(other.m_booleanFunction)
, m_name(other.m_name)
, m_visibleName(other.m_visibleName)
{
}
void Action::connect(Function func) {
m_booleanFunction = {};
m_function = func;
}
void Action::trigger(bool active) {
if (!m_enabled) {
return;
}
if (m_function && active) {
m_function();
}
if (m_booleanFunction) {
m_booleanFunction(active);
}
m_active = active;
emit activated(active);
}
void Action::setEnabled(bool e) {
if (m_enabled == e) {
return;
}
m_enabled = e;
emit enabled(e);
}
void Action::setActive(bool a) {
if (m_active == a) {
return;
}
m_active = a;
emit activated(a);
}
Action& Action::operator=(const Action& other) {
setParent(other.parent());
m_enabled = other.m_enabled;
m_active = other.m_active;
m_function = other.m_function;
m_booleanFunction = other.m_booleanFunction;
m_name = other.m_name;
m_visibleName = other.m_visibleName;
return *this;
}

73
src/platform/qt/Action.h Normal file
View File

@ -0,0 +1,73 @@
/* Copyright (c) 2013-2018 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include <QObject>
#include <functional>
namespace QGBA {
class Action : public QObject {
Q_OBJECT
public:
typedef std::function<void ()> Function;
typedef std::function<void (bool)> BooleanFunction;
Action(Function, const QString& name, const QString& visibleName, QObject* parent = nullptr);
Action(BooleanFunction, const QString& name, const QString& visibleName, QObject* parent = nullptr);
Action(const QString& name, const QString& visibleName, QObject* parent = nullptr);
Action(QObject* parent = nullptr);
Action(Action&);
Action(const Action&);
Function action() const { return m_function; }
BooleanFunction booleanAction() const { return m_booleanFunction; }
const QString& name() const { return m_name; }
const QString& visibleName() const { return m_visibleName; }
bool operator==(const Action& other) const {
if (m_name.isNull()) {
return this == &other;
}
return m_name == other.m_name;
}
void connect(Function);
bool isEnabled() const { return m_enabled; }
bool isActive() const { return m_active; }
bool isExclusive() const { return m_exclusive; }
void setExclusive(bool exclusive = true) { m_exclusive = exclusive; }
Action& operator=(const Action&);
public slots:
void trigger(bool = true);
void setEnabled(bool = true);
void setActive(bool = true);
signals:
void enabled(bool);
void activated(bool);
private:
bool m_enabled = true;
bool m_active = false;
bool m_exclusive = false;
Function m_function;
BooleanFunction m_booleanFunction;
QString m_name;
QString m_visibleName;
};
}

View File

@ -0,0 +1,162 @@
/* Copyright (c) 2013-2018 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ActionMapper.h"
#include "ConfigController.h"
#include "ShortcutController.h"
#include <QMenu>
#include <QMenuBar>
using namespace QGBA;
void ActionMapper::addMenu(const QString& visibleName, const QString& name, const QString& parent) {
QString mname(QString(".%1").arg(name));
m_menus[parent].append(mname);
m_reverseMenus[mname] = parent;
m_menuNames[name] = visibleName;
}
void ActionMapper::addHiddenMenu(const QString& visibleName, const QString& name, const QString& parent) {
m_hiddenActions.insert(QString(".%1").arg(name));
addMenu(visibleName, name, parent);
}
void ActionMapper::clearMenu(const QString& name) {
m_menus[name].clear();
emit menuCleared(name);
}
void ActionMapper::rebuildMenu(QMenuBar* menubar, const ShortcutController& shortcuts) {
menubar->clear();
for (const QString& m : m_menus[{}]) {
if (m_hiddenActions.contains(m)) {
continue;
}
QString menu = m.mid(1);
QMenu* qmenu = menubar->addMenu(m_menuNames[menu]);
rebuildMenu(menu, qmenu, shortcuts);
}
}
void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, const ShortcutController& shortcuts) {
for (const QString& actionName : m_menus[menu]) {
if (actionName.isNull()) {
qmenu->addSeparator();
continue;
}
if (m_hiddenActions.contains(actionName)) {
continue;
}
if (actionName[0] == '.') {
QString name = actionName.mid(1);
QMenu* newMenu = qmenu->addMenu(m_menuNames[name]);
rebuildMenu(name, newMenu, shortcuts);
continue;
}
Action* action = &m_actions[actionName];
QAction* qaction = qmenu->addAction(action->visibleName());
qaction->setEnabled(action->isEnabled());
if (action->isExclusive() || action->booleanAction()) {
qaction->setCheckable(true);
}
if (action->isActive()) {
qaction->setChecked(true);
}
const Shortcut* shortcut = shortcuts.shortcut(actionName);
if (shortcut && shortcut->shortcut() > 0) {
qaction->setShortcut(QKeySequence(shortcut->shortcut()));
} else if (!m_defaultShortcuts[actionName].isEmpty()) {
qaction->setShortcut(m_defaultShortcuts[actionName][0]);
}
QObject::connect(qaction, &QAction::triggered, [qaction, action](bool enabled) {
if (qaction->isCheckable()) {
action->trigger(enabled);
} else {
action->trigger();
}
});
QObject::connect(action, &Action::enabled, qaction, &QAction::setEnabled);
QObject::connect(action, &Action::activated, qaction, &QAction::setChecked);
QObject::connect(action, &Action::destroyed, qaction, &QAction::deleteLater);
if (shortcut) {
QObject::connect(shortcut, &Shortcut::shortcutChanged, qaction, [qaction](int shortcut) {
qaction->setShortcut(QKeySequence(shortcut));
});
}
}
}
void ActionMapper::addSeparator(const QString& menu) {
m_menus[menu].append(QString{});
}
Action* ActionMapper::addAction(const Action& act, const QString& name, const QString& menu, const QKeySequence& shortcut) {
m_actions.insert(name, act);
m_reverseMenus[name] = menu;
m_menus[menu].append(name);
if (!shortcut.isEmpty()) {
m_defaultShortcuts[name] = shortcut;
}
emit actionAdded(name);
return &m_actions[name];
}
Action* ActionMapper::addAction(const QString& visibleName, const QString& name, Action::Function action, const QString& menu, const QKeySequence& shortcut) {
return addAction(Action(action, name, visibleName), name, menu, shortcut);
}
Action* ActionMapper::addAction(const QString& visibleName, ConfigOption* option, const QVariant& variant, const QString& menu) {
return addAction(Action([option, variant]() {
option->setValue(variant);
}, option->name(), visibleName), QString("%1.%2").arg(option->name()).arg(variant.toString()), menu, {});
}
Action* ActionMapper::addBooleanAction(const QString& visibleName, const QString& name, Action::BooleanFunction action, const QString& menu, const QKeySequence& shortcut) {
return addAction(Action(action, name, visibleName), name, menu, shortcut);
}
Action* ActionMapper::addBooleanAction(const QString& visibleName, ConfigOption* option, const QString& menu) {
return addAction(Action([option](bool value) {
option->setValue(value);
}, option->name(), visibleName), option->name(), menu, {});
}
Action* ActionMapper::addHeldAction(const QString& visibleName, const QString& name, Action::BooleanFunction action, const QString& menu, const QKeySequence& shortcut) {
m_hiddenActions.insert(name);
m_heldActions.insert(name);
return addBooleanAction(visibleName, name, action, menu, shortcut);
}
Action* ActionMapper::addHiddenAction(const QString& visibleName, const QString& name, Action::Function action, const QString& menu, const QKeySequence& shortcut) {
m_hiddenActions.insert(name);
return addAction(visibleName, name, action, menu, shortcut);
}
QStringList ActionMapper::menuItems(const QString& menu) const {
return m_menus[menu];
}
QString ActionMapper::menuFor(const QString& menu) const {
return m_reverseMenus[menu];
}
QString ActionMapper::menuName(const QString& menu) const {
if (!menu.isNull() && menu[0] == '.') {
return m_menuNames[menu.mid(1)];
}
return m_menuNames[menu];
}
Action* ActionMapper::getAction(const QString& itemName) {
return &m_actions[itemName];
}
QKeySequence ActionMapper::defaultShortcut(const QString& itemName) {
return m_defaultShortcuts[itemName];
}

View File

@ -0,0 +1,85 @@
/* Copyright (c) 2013-2018 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include "Action.h"
#include <QHash>
#include <QKeySequence>
#include <QObject>
#include <QSet>
#include <functional>
class QMenu;
class QMenuBar;
namespace QGBA {
class ConfigOption;
class ShortcutController;
class ActionMapper : public QObject {
Q_OBJECT
public:
void addMenu(const QString& visibleName, const QString& name, const QString& parent = {});
void addHiddenMenu(const QString& visibleName, const QString& name, const QString& parent = {});
void clearMenu(const QString& name);
void rebuildMenu(QMenuBar*, const ShortcutController&);
void addSeparator(const QString& menu);
Action* addAction(const QString& visibleName, const QString& name, Action::Function action, const QString& menu = {}, const QKeySequence& = {});
template<typename T, typename V> Action* addAction(const QString& visibleName, const QString& name, T* obj, V (T::*method)(), const QString& menu = {}, const QKeySequence& = {});
Action* addAction(const QString& visibleName, ConfigOption* option, const QVariant& variant, const QString& menu = {});
Action* addBooleanAction(const QString& visibleName, const QString& name, Action::BooleanFunction action, const QString& menu = {}, const QKeySequence& = {});
Action* addBooleanAction(const QString& visibleName, ConfigOption* option, const QString& menu = {});
Action* addHeldAction(const QString& visibleName, const QString& name, Action::BooleanFunction action, const QString& menu = {}, const QKeySequence& = {});
Action* addHiddenAction(const QString& visibleName, const QString& name, Action::Function action, const QString& menu = {}, const QKeySequence& = {});
template<typename T, typename V> Action* addHiddenAction(const QString& visibleName, const QString& name, T* obj, V (T::*method)(), const QString& menu = {}, const QKeySequence& = {});
bool isHeld(const QString& name) const { return m_heldActions.contains(name); }
QStringList menuItems(const QString& menu = QString()) const;
QString menuFor(const QString& action) const;
QString menuName(const QString& menu) const;
Action* getAction(const QString& action);
QKeySequence defaultShortcut(const QString& action);
signals:
void actionAdded(const QString& name);
void menuCleared(const QString& name);
private:
void rebuildMenu(const QString& menu, QMenu* qmenu, const ShortcutController&);
Action* addAction(const Action& act, const QString& name, const QString& menu, const QKeySequence& shortcut);
QHash<QString, Action> m_actions;
QHash<QString, QStringList> m_menus;
QHash<QString, QString> m_reverseMenus;
QHash<QString, QString> m_menuNames;
QHash<QString, QKeySequence> m_defaultShortcuts;
QSet<QString> m_hiddenActions;
QSet<QString> m_heldActions;
};
template<typename T, typename V>
Action* ActionMapper::addAction(const QString& visibleName, const QString& name, T* obj, V (T::*method)(), const QString& menu, const QKeySequence& shortcut) {
return addAction(visibleName, name, std::bind(method, obj), menu, shortcut);
}
template<typename T, typename V>
Action* ActionMapper::addHiddenAction(const QString& visibleName, const QString& name, T* obj, V (T::*method)(), const QString& menu, const QKeySequence& shortcut) {
m_hiddenActions.insert(name);
return addAction(visibleName, name, obj, method, menu, shortcut);
}
}

View File

@ -68,6 +68,8 @@ endif()
set(SOURCE_FILES
AboutScreen.cpp
AbstractUpdater.cpp
Action.cpp
ActionMapper.cpp
AssetTile.cpp
AssetView.cpp
AudioProcessor.cpp
@ -108,6 +110,8 @@ set(SOURCE_FILES
SensorView.cpp
SettingsView.cpp
ShaderSelector.cpp
ShortcutController.cpp
ShortcutModel.cpp
ShortcutView.cpp
Swatch.cpp
TilePainter.cpp

View File

@ -5,9 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ConfigController.h"
#include "ActionMapper.h"
#include "CoreController.h"
#include <QAction>
#include <QDir>
#include <QMenu>
@ -15,51 +15,58 @@
using namespace QGBA;
ConfigOption::ConfigOption(QObject* parent)
ConfigOption::ConfigOption(const QString& name, QObject* parent)
: QObject(parent)
, m_name(name)
{
}
void ConfigOption::connect(std::function<void(const QVariant&)> slot, QObject* parent) {
m_slots[parent] = slot;
QObject::connect(parent, &QAction::destroyed, [this, slot, parent]() {
QObject::connect(parent, &QObject::destroyed, [this, slot, parent]() {
m_slots.remove(parent);
});
}
QAction* ConfigOption::addValue(const QString& text, const QVariant& value, QMenu* parent) {
QAction* action = new QAction(text, parent);
action->setCheckable(true);
QObject::connect(action, &QAction::triggered, [this, value]() {
Action* ConfigOption::addValue(const QString& text, const QVariant& value, ActionMapper* actions, const QString& menu) {
Action* action;
auto function = [this, value]() {
emit valueChanged(value);
});
if (parent) {
QObject::connect(parent, &QAction::destroyed, [this, action, value]() {
m_actions.removeAll(qMakePair(action, value));
});
parent->addAction(action);
};
QString name = QString("%1.%2").arg(m_name).arg(value.toString());
if (actions) {
action = actions->addAction(text, name, function, menu);
} else {
action = new Action(function, name, text);
}
m_actions.append(qMakePair(action, value));
action->setExclusive();
QObject::connect(action, &QObject::destroyed, [this, action, value]() {
m_actions.removeAll(std::make_pair(action, value));
});
m_actions.append(std::make_pair(action, value));
return action;
}
QAction* ConfigOption::addValue(const QString& text, const char* value, QMenu* parent) {
return addValue(text, QString(value), parent);
Action* ConfigOption::addValue(const QString& text, const char* value, ActionMapper* actions, const QString& menu) {
return addValue(text, QString(value), actions, menu);
}
QAction* ConfigOption::addBoolean(const QString& text, QMenu* parent) {
QAction* action = new QAction(text, parent);
action->setCheckable(true);
QObject::connect(action, &QAction::triggered, [this, action]() {
emit valueChanged(action->isChecked());
});
if (parent) {
QObject::connect(parent, &QAction::destroyed, [this, action]() {
m_actions.removeAll(qMakePair(action, 1));
});
parent->addAction(action);
Action* ConfigOption::addBoolean(const QString& text, ActionMapper* actions, const QString& menu) {
Action* action;
auto function = [this](bool value) {
emit valueChanged(value);
};
if (actions) {
action = actions->addBooleanAction(text, m_name, function, menu);
} else {
action = new Action(function, m_name, text);
}
m_actions.append(qMakePair(action, 1));
QObject::connect(action, &QObject::destroyed, [this, action]() {
m_actions.removeAll(std::make_pair(action, 1));
});
m_actions.append(std::make_pair(action, 1));
return action;
}
@ -80,10 +87,8 @@ void ConfigOption::setValue(const char* value) {
}
void ConfigOption::setValue(const QVariant& value) {
for (QPair<QAction*, QVariant>& action : m_actions) {
bool signalsEnabled = action.first->blockSignals(true);
action.first->setChecked(value == action.second);
action.first->blockSignals(signalsEnabled);
for (std::pair<Action*, QVariant>& action : m_actions) {
action.first->setActive(value == action.second);
}
for (std::function<void(const QVariant&)>& slot : m_slots.values()) {
slot(value);
@ -142,7 +147,7 @@ ConfigOption* ConfigController::addOption(const char* key) {
if (m_optionSet.contains(optionName)) {
return m_optionSet[optionName];
}
ConfigOption* newOption = new ConfigOption(this);
ConfigOption* newOption = new ConfigOption(optionName, this);
m_optionSet[optionName] = newOption;
connect(newOption, &ConfigOption::valueChanged, [this, key](const QVariant& value) {
setOption(key, value);

View File

@ -18,7 +18,6 @@
#include <mgba-util/configuration.h>
#include <mgba/feature/commandline.h>
class QAction;
class QMenu;
struct mArguments;
@ -26,17 +25,22 @@ struct GBACartridgeOverride;
namespace QGBA {
class Action;
class ActionMapper;
class ConfigOption : public QObject {
Q_OBJECT
public:
ConfigOption(QObject* parent = nullptr);
ConfigOption(const QString& name, QObject* parent = nullptr);
void connect(std::function<void(const QVariant&)>, QObject* parent = nullptr);
QAction* addValue(const QString& text, const QVariant& value, QMenu* parent = nullptr);
QAction* addValue(const QString& text, const char* value, QMenu* parent = nullptr);
QAction* addBoolean(const QString& text, QMenu* parent = nullptr);
Action* addValue(const QString& text, const QVariant& value, ActionMapper* actions = nullptr, const QString& menu = {});
Action* addValue(const QString& text, const char* value, ActionMapper* actions = nullptr, const QString& menu = {});
Action* addBoolean(const QString& text, ActionMapper* actions = nullptr, const QString& menu = {});
QString name() const { return m_name; }
public slots:
void setValue(bool value);
@ -50,7 +54,8 @@ signals:
private:
QMap<QObject*, std::function<void(const QVariant&)>> m_slots;
QList<QPair<QAction*, QVariant>> m_actions;
QList<std::pair<Action*, QVariant>> m_actions;
QString m_name;
};
class ConfigController : public QObject {

View File

@ -1,95 +0,0 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include "GamepadAxisEvent.h"
#include <mgba/gba/interface.h>
namespace QGBA {
class InputController;
class InputProfile {
public:
static const InputProfile* findProfile(const QString& name);
void apply(InputController*) const;
bool lookupShortcutButton(const QString& shortcut, int* button) const;
bool lookupShortcutAxis(const QString& shortcut, int* axis, GamepadAxisEvent::Direction* direction) const;
private:
struct Coord {
int x;
int y;
};
struct AxisValue {
GamepadAxisEvent::Direction direction;
int axis;
};
template <typename T> struct Shortcuts {
T loadState;
T saveState;
T holdFastForward;
T holdRewind;
};
struct Axis {
GamepadAxisEvent::Direction direction;
int axis;
};
template <typename T> struct KeyList {
T keyA;
T keyB;
T keySelect;
T keyStart;
T keyRight;
T keyLeft;
T keyUp;
T keyDown;
T keyR;
T keyL;
};
constexpr InputProfile(const char* name,
const KeyList<int> keys,
const Shortcuts<int> shortcutButtons = { -1, -1, -1, -1},
const Shortcuts<Axis> shortcutAxes = {
{GamepadAxisEvent::Direction::NEUTRAL, -1},
{GamepadAxisEvent::Direction::NEUTRAL, -1},
{GamepadAxisEvent::Direction::NEUTRAL, -1},
{GamepadAxisEvent::Direction::NEUTRAL, -1}},
const KeyList<AxisValue> axes = {
{ GamepadAxisEvent::Direction::NEUTRAL, -1 },
{ GamepadAxisEvent::Direction::NEUTRAL, -1 },
{ GamepadAxisEvent::Direction::NEUTRAL, -1 },
{ GamepadAxisEvent::Direction::NEUTRAL, -1 },
{ GamepadAxisEvent::Direction::POSITIVE, 0 },
{ GamepadAxisEvent::Direction::NEGATIVE, 0 },
{ GamepadAxisEvent::Direction::NEGATIVE, 1 },
{ GamepadAxisEvent::Direction::POSITIVE, 1 },
{ GamepadAxisEvent::Direction::NEUTRAL, -1 },
{ GamepadAxisEvent::Direction::NEUTRAL, -1 }},
const struct Coord& tiltAxis = { 2, 3 },
const struct Coord& gyroAxis = { 0, 1 },
float gyroSensitivity = 2e+09f);
static const InputProfile s_defaultMaps[];
const char* m_profileName;
const int m_keys[GBA_KEY_MAX];
const AxisValue m_axes[GBA_KEY_MAX];
const Shortcuts<int> m_shortcutButtons;
const Shortcuts<Axis> m_shortcutAxes;
Coord m_tiltAxis;
Coord m_gyroAxis;
float m_gyroSensitivity;
};
}

View File

@ -313,17 +313,6 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
m_ui.logFile->setText(path);
}
});
m_keyView = new ShortcutView();
m_keyView->setModel(inputController->keyIndex());
m_keyView->setInputController(inputController);
m_shortcutView = new ShortcutView();
m_shortcutView->setModel(inputController->inputIndex());
m_shortcutView->setInputController(inputController);
m_ui.stackedWidget->addWidget(m_keyView);
m_ui.tabs->addItem(tr("Controls"));
m_ui.stackedWidget->addWidget(m_shortcutView);
m_ui.tabs->addItem(tr("Shortcuts"));
}
SettingsView::~SettingsView() {
@ -497,8 +486,6 @@ void SettingsView::updateConfig() {
m_controller->write();
m_input->rebuildIndex(m_shortcutView->root());
m_input->rebuildKeyIndex(m_keyView->root());
m_input->saveConfiguration();
emit pathsChanged();

View File

@ -23,7 +23,6 @@ namespace QGBA {
class ConfigController;
class InputController;
class InputIndex;
class ShortcutView;
class ShaderSelector;
class SettingsView : public QDialog {
@ -55,8 +54,6 @@ private:
ConfigController* m_controller;
InputController* m_input;
ShortcutView* m_shortcutView;
ShortcutView* m_keyView;
ShaderSelector* m_shader = nullptr;
LogConfigModel m_logModel;

View File

@ -0,0 +1,438 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ShortcutController.h"
#include "ConfigController.h"
#include "GamepadButtonEvent.h"
#include "InputProfile.h"
#include <QAction>
#include <QKeyEvent>
#include <QMenu>
#include <QRegularExpression>
using namespace QGBA;
ShortcutController::ShortcutController(QObject* parent)
: QObject(parent)
{
}
void ShortcutController::setConfigController(ConfigController* controller) {
m_config = controller;
}
void ShortcutController::setActionMapper(ActionMapper* actions) {
m_actions = actions;
connect(actions, &ActionMapper::actionAdded, this, &ShortcutController::generateItem);
connect(actions, &ActionMapper::menuCleared, this, &ShortcutController::menuCleared);
rebuildItems();
}
void ShortcutController::updateKey(const QString& name, int keySequence) {
auto item = m_items[name];
if (!item) {
return;
}
updateKey(item, keySequence);
if (m_config) {
m_config->setQtOption(item->name(), QKeySequence(keySequence).toString(), KEY_SECTION);
}
}
void ShortcutController::updateKey(std::shared_ptr<Shortcut> item, int keySequence) {
int oldShortcut = item->shortcut();
if (m_actions->isHeld(item->name())) {
if (oldShortcut > 0) {
m_heldKeys.take(oldShortcut);
}
if (keySequence > 0) {
m_heldKeys[keySequence] = item;
}
}
item->setShortcut(keySequence);
}
void ShortcutController::updateButton(const QString& name, int button) {
auto item = m_items[name];
if (!item) {
return;
}
int oldButton = item->button();
if (oldButton >= 0) {
m_buttons.take(oldButton);
}
item->setButton(button);
if (button >= 0) {
clearAxis(name);
m_buttons[button] = item;
}
if (m_config) {
m_config->setQtOption(name, button, BUTTON_SECTION);
if (!m_profileName.isNull()) {
m_config->setQtOption(name, button, BUTTON_PROFILE_SECTION + m_profileName);
}
}
}
void ShortcutController::updateAxis(const QString& name, int axis, GamepadAxisEvent::Direction direction) {
auto item = m_items[name];
if (!item) {
return;
}
int oldAxis = item->axis();
GamepadAxisEvent::Direction oldDirection = item->direction();
if (oldAxis >= 0) {
m_axes.take(std::make_pair(oldAxis, oldDirection));
}
if (axis >= 0 && direction != GamepadAxisEvent::NEUTRAL) {
clearButton(name);
m_axes[std::make_pair(axis, direction)] = item;
}
item->setAxis(axis, direction);
if (m_config) {
char d = '\0';
if (direction == GamepadAxisEvent::POSITIVE) {
d = '+';
}
if (direction == GamepadAxisEvent::NEGATIVE) {
d = '-';
}
m_config->setQtOption(name, QString("%1%2").arg(d).arg(axis), AXIS_SECTION);
if (!m_profileName.isNull()) {
m_config->setQtOption(name, QString("%1%2").arg(d).arg(axis), AXIS_PROFILE_SECTION + m_profileName);
}
}
}
void ShortcutController::clearKey(const QString& name) {
updateKey(name, 0);
}
void ShortcutController::clearButton(const QString& name) {
updateButton(name, -1);
}
void ShortcutController::clearAxis(const QString& name) {
updateAxis(name, -1, GamepadAxisEvent::NEUTRAL);
}
void ShortcutController::rebuildItems() {
m_items.clear();
m_buttons.clear();
m_axes.clear();
m_heldKeys.clear();
onSubitems({}, std::bind(&ShortcutController::generateItem, this, std::placeholders::_1));
}
bool ShortcutController::eventFilter(QObject*, QEvent* event) {
if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->isAutoRepeat()) {
return false;
}
int key = keyEvent->key();
if (!isModifierKey(key)) {
key |= (keyEvent->modifiers() & ~Qt::KeypadModifier);
} else {
key = toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier));
}
auto item = m_heldKeys.find(key);
if (item != m_heldKeys.end()) {
Action::BooleanFunction fn = item.value()->action()->booleanAction();
fn(event->type() == QEvent::KeyPress);
event->accept();
return true;
}
}
if (event->type() == GamepadButtonEvent::Down()) {
auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value());
if (item == m_buttons.end()) {
return false;
}
Action* action = item.value()->action();
if (action) {
action->trigger();
}
event->accept();
return true;
}
if (event->type() == GamepadButtonEvent::Up()) {
auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value());
if (item == m_buttons.end()) {
return false;
}
Action* action = item.value()->action();
if (action) {
action->trigger(false);
}
event->accept();
return true;
}
if (event->type() == GamepadAxisEvent::Type()) {
GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
auto item = m_axes.find(std::make_pair(gae->axis(), gae->direction()));
if (item == m_axes.end()) {
return false;
}
Action* action = item.value()->action();
if (action) {
action->trigger(gae->isNew());
}
event->accept();
return true;
}
return false;
}
void ShortcutController::generateItem(const QString& itemName) {
if (itemName.isNull() || itemName[0] == '.') {
return;
}
Action* action = m_actions->getAction(itemName);
if (action) {
std::shared_ptr<Shortcut> item = std::make_shared<Shortcut>(action);
m_items[itemName] = item;
loadShortcuts(item);
}
emit shortcutAdded(itemName);
}
bool ShortcutController::loadShortcuts(std::shared_ptr<Shortcut> item) {
if (item->name().isNull()) {
return false;
}
loadGamepadShortcuts(item);
QVariant shortcut = m_config->getQtOption(item->name(), KEY_SECTION);
if (!shortcut.isNull()) {
if (shortcut.toString().endsWith("+")) {
updateKey(item, toModifierShortcut(shortcut.toString()));
} else {
updateKey(item, QKeySequence(shortcut.toString())[0]);
}
return true;
} else {
QKeySequence defaultShortcut = m_actions->defaultShortcut(item->name());
if (!defaultShortcut.isEmpty()) {
updateKey(item, defaultShortcut[0]);
return true;
}
}
return false;
}
void ShortcutController::loadGamepadShortcuts(std::shared_ptr<Shortcut> item) {
if (item->name().isNull()) {
return;
}
QVariant button = m_config->getQtOption(item->name(), !m_profileName.isNull() ? BUTTON_PROFILE_SECTION + m_profileName : BUTTON_SECTION);
int oldButton = item->button();
if (oldButton >= 0) {
m_buttons.take(oldButton);
item->setButton(-1);
}
if (!button.isNull()) {
item->setButton(button.toInt());
m_buttons[button.toInt()] = item;
}
QVariant axis = m_config->getQtOption(item->name(), !m_profileName.isNull() ? AXIS_PROFILE_SECTION + m_profileName : AXIS_SECTION);
int oldAxis = item->axis();
GamepadAxisEvent::Direction oldDirection = item->direction();
if (oldAxis >= 0) {
m_axes.take(std::make_pair(oldAxis, oldDirection));
item->setAxis(-1, GamepadAxisEvent::NEUTRAL);
}
if (!axis.isNull()) {
QString axisDesc = axis.toString();
if (axisDesc.size() >= 2) {
GamepadAxisEvent::Direction direction = GamepadAxisEvent::NEUTRAL;
if (axisDesc[0] == '-') {
direction = GamepadAxisEvent::NEGATIVE;
}
if (axisDesc[0] == '+') {
direction = GamepadAxisEvent::POSITIVE;
}
bool ok;
int axis = axisDesc.mid(1).toInt(&ok);
if (ok) {
item->setAxis(axis, direction);
m_axes[std::make_pair(axis, direction)] = item;
}
}
}
}
void ShortcutController::loadProfile(const QString& profile) {
m_profileName = profile;
m_profile = InputProfile::findProfile(profile);
onSubitems({}, [this](std::shared_ptr<Shortcut> item) {
loadGamepadShortcuts(item);
});
}
void ShortcutController::onSubitems(const QString& menu, std::function<void(std::shared_ptr<Shortcut>)> func) {
for (const QString& subitem : m_actions->menuItems(menu)) {
auto item = m_items[subitem];
if (item) {
func(item);
}
if (subitem.size() && subitem[0] == '.') {
onSubitems(subitem.mid(1), func);
}
}
}
void ShortcutController::onSubitems(const QString& menu, std::function<void(const QString&)> func) {
for (const QString& subitem : m_actions->menuItems(menu)) {
func(subitem);
if (subitem.size() && subitem[0] == '.') {
onSubitems(subitem.mid(1), func);
}
}
}
int ShortcutController::toModifierShortcut(const QString& shortcut) {
// Qt doesn't seem to work with raw modifier shortcuts!
QStringList modifiers = shortcut.split('+');
int value = 0;
for (const auto& mod : modifiers) {
if (mod == QLatin1String("Shift")) {
value |= Qt::ShiftModifier;
continue;
}
if (mod == QLatin1String("Ctrl")) {
value |= Qt::ControlModifier;
continue;
}
if (mod == QLatin1String("Alt")) {
value |= Qt::AltModifier;
continue;
}
if (mod == QLatin1String("Meta")) {
value |= Qt::MetaModifier;
continue;
}
}
return value;
}
bool ShortcutController::isModifierKey(int key) {
switch (key) {
case Qt::Key_Shift:
case Qt::Key_Control:
case Qt::Key_Alt:
case Qt::Key_Meta:
return true;
default:
return false;
}
}
int ShortcutController::toModifierKey(int key) {
int modifiers = key & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier);
key ^= modifiers;
switch (key) {
case Qt::Key_Shift:
modifiers |= Qt::ShiftModifier;
break;
case Qt::Key_Control:
modifiers |= Qt::ControlModifier;
break;
case Qt::Key_Alt:
modifiers |= Qt::AltModifier;
break;
case Qt::Key_Meta:
modifiers |= Qt::MetaModifier;
break;
default:
break;
}
return modifiers;
}
const Shortcut* ShortcutController::shortcut(const QString& action) const {
return m_items[action].get();
}
QString ShortcutController::name(int index, const QString& parent) const {
QStringList menu = m_actions->menuItems(parent.isNull() || parent[0] != '.' ? parent : parent.mid(1));
menu.removeAll({});
if (index >= menu.size()) {
return {};
}
return menu[index];
}
QString ShortcutController::parent(const QString& action) const {
return QString(".%1").arg(m_actions->menuFor(action));
}
QString ShortcutController::visibleName(const QString& action) const {
if (action.isNull()) {
return {};
}
QString name;
if (action[0] == '.') {
name = m_actions->menuName(action.mid(1));
} else {
name = m_actions->getAction(action)->visibleName();
}
return name.replace(QRegularExpression("&(.)"), "\\1");
}
int ShortcutController::indexIn(const QString& action) const {
QString name = m_actions->menuFor(action);
QStringList menu = m_actions->menuItems(name);
menu.removeAll({});
return menu.indexOf(action);
}
int ShortcutController::count(const QString& name) const {
QStringList menu;
if (name.isNull()) {
menu = m_actions->menuItems();
} else if (name[0] != '.') {
return 0;
} else {
menu = m_actions->menuItems(name.mid(1));
}
menu.removeAll({});
return menu.count();
}
Shortcut::Shortcut(Action* action)
: m_action(action)
{
}
void Shortcut::setShortcut(int shortcut) {
if (m_shortcut == shortcut) {
return;
}
m_shortcut = shortcut;
emit shortcutChanged(shortcut);
}
void Shortcut::setButton(int button) {
if (m_button == button) {
return;
}
m_button = button;
emit buttonChanged(button);
}
void Shortcut::setAxis(int axis, GamepadAxisEvent::Direction direction) {
if (m_axis == axis && m_direction == direction) {
return;
}
m_axis = axis;
m_direction = direction;
emit axisChanged(axis, direction);
}

View File

@ -5,23 +5,61 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include "ActionMapper.h"
#include "GamepadAxisEvent.h"
#include <QAbstractItemModel>
#include <QHash>
#include <QMap>
#include <QObject>
#include <QString>
#include <functional>
#include <memory>
class QAction;
class QKeyEvent;
class QMenu;
class QString;
namespace QGBA {
class ConfigController;
class InputProfile;
class ShortcutController : public QAbstractItemModel {
class Shortcut : public QObject {
Q_OBJECT
public:
Shortcut(Action* action);
Action* action() { return m_action; }
const Action* action() const { return m_action; }
const int shortcut() const { return m_shortcut; }
QString visibleName() const { return m_action ? m_action->visibleName() : QString(); }
QString name() const { return m_action ? m_action->name() : QString(); }
int button() const { return m_button; }
int axis() const { return m_axis; }
GamepadAxisEvent::Direction direction() const { return m_direction; }
bool operator==(const Shortcut& other) const {
return m_action == other.m_action;
}
public slots:
void setShortcut(int sequence);
void setButton(int button);
void setAxis(int axis, GamepadAxisEvent::Direction direction);
signals:
void shortcutChanged(int sequence);
void buttonChanged(int button);
void axisChanged(int axis, GamepadAxisEvent::Direction direction);
private:
Action* m_action = nullptr;
int m_shortcut = 0;
int m_button = -1;
int m_axis = -1;
GamepadAxisEvent::Direction m_direction;
};
class ShortcutController : public QObject {
Q_OBJECT
private:
@ -31,112 +69,57 @@ private:
constexpr static const char* const BUTTON_PROFILE_SECTION = "shortcutProfileButton.";
constexpr static const char* const AXIS_PROFILE_SECTION = "shortcutProfileAxis.";
class ShortcutItem {
public:
typedef QPair<std::function<void ()>, std::function<void ()>> Functions;
ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent = nullptr);
ShortcutItem(Functions functions, int shortcut, const QString& visibleName, const QString& name,
ShortcutItem* parent = nullptr);
ShortcutItem(QMenu* action, ShortcutItem* parent = nullptr);
QAction* action() { return m_action; }
const QAction* action() const { return m_action; }
const int shortcut() const { return m_shortcut; }
Functions functions() const { return m_functions; }
QMenu* menu() { return m_menu; }
const QMenu* menu() const { return m_menu; }
const QString& visibleName() const { return m_visibleName; }
const QString& name() const { return m_name; }
QList<ShortcutItem>& items() { return m_items; }
const QList<ShortcutItem>& items() const { return m_items; }
ShortcutItem* parent() { return m_parent; }
const ShortcutItem* parent() const { return m_parent; }
void addAction(QAction* action, const QString& name);
void addFunctions(Functions functions, int shortcut, const QString& visibleName,
const QString& name);
void addSubmenu(QMenu* menu);
int button() const { return m_button; }
void setShortcut(int sequence);
void setButton(int button) { m_button = button; }
int axis() const { return m_axis; }
GamepadAxisEvent::Direction direction() const { return m_direction; }
void setAxis(int axis, GamepadAxisEvent::Direction direction);
bool operator==(const ShortcutItem& other) const {
return m_menu == other.m_menu && m_action == other.m_action;
}
private:
QAction* m_action = nullptr;
int m_shortcut = 0;
QMenu* m_menu = nullptr;
Functions m_functions;
QString m_name;
QString m_visibleName;
int m_button = -1;
int m_axis = -1;
GamepadAxisEvent::Direction m_direction;
QList<ShortcutItem> m_items;
ShortcutItem* m_parent;
};
public:
ShortcutController(QObject* parent = nullptr);
void setConfigController(ConfigController* controller);
void setActionMapper(ActionMapper* actionMapper);
void setProfile(const QString& profile);
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
void updateKey(const QString& action, int keySequence);
void updateButton(const QString& action, int button);
void updateAxis(const QString& action, int axis, GamepadAxisEvent::Direction direction);
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
virtual QModelIndex parent(const QModelIndex& index) const override;
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
void addAction(QMenu* menu, QAction* action, const QString& name);
void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
int shortcut, const QString& visibleName, const QString& name);
void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
const QKeySequence& shortcut, const QString& visibleName, const QString& name);
void addMenu(QMenu* menu, QMenu* parent = nullptr);
QAction* getAction(const QString& name);
int shortcutAt(const QModelIndex& index) const;
bool isMenuAt(const QModelIndex& index) const;
void updateKey(const QModelIndex& index, int keySequence);
void updateButton(const QModelIndex& index, int button);
void updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction);
void clearKey(const QModelIndex& index);
void clearButton(const QModelIndex& index);
void clearKey(const QString& action);
void clearButton(const QString& action);
void clearAxis(const QString& action);
static int toModifierShortcut(const QString& shortcut);
static bool isModifierKey(int key);
static int toModifierKey(int key);
const Shortcut* shortcut(const QString& action) const;
int indexIn(const QString& action) const;
int count(const QString& menu = {}) const;
QString parent(const QString& action) const;
QString name(int index, const QString& parent = {}) const;
QString visibleName(const QString& item) const;
signals:
void shortcutAdded(const QString& name);
void menuCleared(const QString& name);
public slots:
void loadProfile(const QString& profile);
void rebuildItems();
protected:
bool eventFilter(QObject*, QEvent*) override;
private:
ShortcutItem* itemAt(const QModelIndex& index);
const ShortcutItem* itemAt(const QModelIndex& index) const;
bool loadShortcuts(ShortcutItem*);
void loadGamepadShortcuts(ShortcutItem*);
void onSubitems(ShortcutItem*, std::function<void(ShortcutItem*)> func);
void updateKey(ShortcutItem* item, int keySequence);
void generateItem(const QString& itemName);
bool loadShortcuts(std::shared_ptr<Shortcut>);
void loadGamepadShortcuts(std::shared_ptr<Shortcut>);
void onSubitems(const QString& menu, std::function<void(std::shared_ptr<Shortcut>)> func);
void onSubitems(const QString& menu, std::function<void(const QString&)> func);
void updateKey(std::shared_ptr<Shortcut> item, int keySequence);
ShortcutItem m_rootMenu{nullptr};
QMap<QMenu*, ShortcutItem*> m_menuMap;
QMap<int, ShortcutItem*> m_buttons;
QMap<QPair<int, GamepadAxisEvent::Direction>, ShortcutItem*> m_axes;
QMap<int, ShortcutItem*> m_heldKeys;
QHash<QString, std::shared_ptr<Shortcut>> m_items;
QHash<int, std::shared_ptr<Shortcut>> m_buttons;
QMap<std::pair<int, GamepadAxisEvent::Direction>, std::shared_ptr<Shortcut>> m_axes;
QHash<int, std::shared_ptr<Shortcut>> m_heldKeys;
ActionMapper* m_actions = nullptr;
ConfigController* m_config = nullptr;
QString m_profileName;
const InputProfile* m_profile = nullptr;

View File

@ -0,0 +1,138 @@
/* Copyright (c) 2013-2019 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ShortcutModel.h"
#include "ShortcutController.h"
using namespace QGBA;
ShortcutModel::ShortcutModel(QObject* parent)
: QAbstractItemModel(parent)
{
}
void ShortcutModel::setController(ShortcutController* controller) {
beginResetModel();
m_controller = controller;
m_cache.clear();
connect(controller, &ShortcutController::shortcutAdded, this, &ShortcutModel::addRowNamed);
connect(controller, &ShortcutController::menuCleared, this, &ShortcutModel::clearMenu);
endResetModel();
}
QVariant ShortcutModel::data(const QModelIndex& index, int role) const {
if (role != Qt::DisplayRole || !index.isValid()) {
return QVariant();
}
int row = index.row();
const Item* item = static_cast<Item*>(index.internalPointer());
const Shortcut* shortcut = item->shortcut;
switch (index.column()) {
case 0:
return m_controller->visibleName(item->name);
case 1:
return shortcut ? QKeySequence(shortcut->shortcut()).toString(QKeySequence::NativeText) : QVariant();
case 2:
if (!shortcut) {
return QVariant();
}
if (shortcut->button() >= 0) {
return shortcut->button();
}
if (shortcut->axis() >= 0) {
char d = '\0';
if (shortcut->direction() == GamepadAxisEvent::POSITIVE) {
d = '+';
}
if (shortcut->direction() == GamepadAxisEvent::NEGATIVE) {
d = '-';
}
return QString("%1%2").arg(d).arg(shortcut->axis());
}
break;
}
return QVariant();
}
QVariant ShortcutModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (role != Qt::DisplayRole) {
return QAbstractItemModel::headerData(section, orientation, role);
}
if (orientation == Qt::Horizontal) {
switch (section) {
case 0:
return tr("Action");
case 1:
return tr("Keyboard");
case 2:
return tr("Gamepad");
}
}
return section;
}
QModelIndex ShortcutModel::index(int row, int column, const QModelIndex& parent) const {
QString pmenu;
if (parent.isValid()) {
pmenu = static_cast<Item*>(parent.internalPointer())->name;
}
QString name = m_controller->name(row, pmenu);
Item* item = &(*const_cast<QHash<QString, Item>*>(&m_cache))[name];
item->name = name;
item->shortcut = m_controller->shortcut(name);
return createIndex(row, column, item);
}
QModelIndex ShortcutModel::parent(const QModelIndex& index) const {
if (!index.isValid() || !index.internalPointer()) {
return QModelIndex();
}
Item* item = static_cast<Item*>(index.internalPointer());
QString parent = m_controller->parent(item->name);
if (parent.isNull()) {
return QModelIndex();
}
Item* pitem = &(*const_cast<QHash<QString, Item>*>(&m_cache))[parent];
pitem->name = parent;
pitem->shortcut = m_controller->shortcut(parent);
return createIndex(m_controller->indexIn(parent), 0, pitem);
}
int ShortcutModel::columnCount(const QModelIndex& index) const {
return 3;
}
int ShortcutModel::rowCount(const QModelIndex& index) const {
if (!index.isValid()) {
return m_controller->count();
}
Item* item = static_cast<Item*>(index.internalPointer());
return m_controller->count(item->name);
}
QString ShortcutModel::name(const QModelIndex& index) const {
if (!index.isValid()) {
return {};
}
Item* item = static_cast<Item*>(index.internalPointer());
return item->name;
}
void ShortcutModel::addRowNamed(const QString& name) {
QString parent = m_controller->parent(name);
Item* item = &m_cache[parent];
item->name = parent;
item->shortcut = m_controller->shortcut(parent);
int index = m_controller->indexIn(name);
beginInsertRows(createIndex(m_controller->indexIn(parent), 0, item), index, index + 1);
endInsertRows();
}
void ShortcutModel::clearMenu(const QString& name) {
// TODO
beginResetModel();
endResetModel();
}

View File

@ -0,0 +1,50 @@
/* Copyright (c) 2013-2019 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include <QAbstractListModel>
namespace QGBA {
class ShortcutController;
class Shortcut;
class ShortcutModel : public QAbstractItemModel {
Q_OBJECT
public:
ShortcutModel(QObject* parent = nullptr);
void setController(ShortcutController* controller);
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
virtual QModelIndex parent(const QModelIndex& index) const override;
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QString name(const QModelIndex&) const;
private slots:
void addRowNamed(const QString&);
void clearMenu(const QString&);
private:
ShortcutController* m_controller = nullptr;
struct Item {
QString name;
const Shortcut* shortcut = nullptr;
};
QHash<QString, Item> m_cache;
};
}

View File

@ -7,7 +7,8 @@
#include "GamepadButtonEvent.h"
#include "InputController.h"
#include "InputModel.h"
#include "ShortcutController.h"
#include "ShortcutModel.h"
#include <QKeyEvent>
@ -15,11 +16,9 @@ using namespace QGBA;
ShortcutView::ShortcutView(QWidget* parent)
: QWidget(parent)
, m_model()
{
m_ui.setupUi(this);
m_ui.keyEdit->setValueKey(0);
m_ui.shortcutTable->setModel(&m_model);
connect(m_ui.gamepadButton, &QAbstractButton::pressed, [this]() {
bool signalsBlocked = m_ui.keyEdit->blockSignals(true);
@ -35,19 +34,17 @@ ShortcutView::ShortcutView(QWidget* parent)
connect(m_ui.keyEdit, &KeyEditor::axisChanged, this, &ShortcutView::updateAxis);
connect(m_ui.shortcutTable, &QAbstractItemView::doubleClicked, this, &ShortcutView::load);
connect(m_ui.clearButton, &QAbstractButton::clicked, this, &ShortcutView::clear);
#ifdef BUILD_SDL
connect(m_ui.gamepadName, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [this](int index) {
m_input->setGamepad(SDL_BINDING_BUTTON, index);
});
#endif
}
ShortcutView::~ShortcutView() {
m_input->releaseFocus(this);
}
void ShortcutView::setModel(InputIndex* model) {
m_model.clone(*model);
void ShortcutView::setController(ShortcutController* controller) {
m_controller = controller;
m_model = new ShortcutModel(this);
m_model->setController(controller);
m_ui.shortcutTable->setModel(m_model);
}
void ShortcutView::setInputController(InputController* controller) {
@ -56,30 +53,15 @@ void ShortcutView::setInputController(InputController* controller) {
}
m_input = controller;
m_input->stealFocus(this);
updateGamepads();
}
void ShortcutView::updateGamepads() {
if (!m_input) {
return;
}
#ifdef BUILD_SDL
m_ui.gamepadName->clear();
QStringList gamepads = m_input->connectedGamepads(SDL_BINDING_BUTTON);
int activeGamepad = m_input->gamepad(SDL_BINDING_BUTTON);
for (const auto& gamepad : gamepads) {
m_ui.gamepadName->addItem(gamepad);
}
m_ui.gamepadName->setCurrentIndex(activeGamepad);
#endif
}
void ShortcutView::load(const QModelIndex& index) {
InputItem* item = m_model.itemAt(index);
if (!item) {
if (!m_controller) {
return;
}
QString name = m_model->name(index);
const Shortcut* item = m_controller->shortcut(name);
if (!item->action()) {
return;
}
int shortcut = item->shortcut();
@ -99,38 +81,51 @@ void ShortcutView::load(const QModelIndex& index) {
}
void ShortcutView::clear() {
if (!m_controller) {
return;
}
QModelIndex index = m_ui.shortcutTable->selectionModel()->currentIndex();
InputItem* item = m_model.itemAt(index);
if (!item) {
QString name = m_model->name(index);
const Shortcut* item = m_controller->shortcut(name);
if (!item->action()) {
return;
}
if (m_ui.gamepadButton->isChecked()) {
item->clearButton();
m_controller->clearButton(name);
m_controller->clearAxis(name);
m_ui.keyEdit->setValueButton(-1);
} else {
item->clearShortcut();
m_controller->clearKey(name);
m_ui.keyEdit->setValueKey(-1);
}
}
void ShortcutView::updateButton(int button) {
InputItem* item = m_model.itemAt(m_ui.shortcutTable->selectionModel()->currentIndex());
if (!item) {
if (!m_controller) {
return;
}
QString name = m_model->name(m_ui.shortcutTable->selectionModel()->currentIndex());
const Shortcut* item = m_controller->shortcut(name);
if (!item->action()) {
return;
}
if (m_ui.gamepadButton->isChecked()) {
item->setButton(button);
m_controller->updateButton(name, button);
} else {
item->setShortcut(button);
m_controller->updateKey(name, button);
}
}
void ShortcutView::updateAxis(int axis, int direction) {
InputItem* item = m_model.itemAt(m_ui.shortcutTable->selectionModel()->currentIndex());
if (!item) {
if (!m_controller) {
return;
}
item->setAxis(axis, static_cast<GamepadAxisEvent::Direction>(direction));
QString name = m_model->name(m_ui.shortcutTable->selectionModel()->currentIndex());
const Shortcut* item = m_controller->shortcut(name);
if (!item->action()) {
return;
}
m_controller->updateAxis(name, axis, static_cast<GamepadAxisEvent::Direction>(direction));
}
void ShortcutView::closeEvent(QCloseEvent*) {

View File

@ -16,6 +16,8 @@
namespace QGBA {
class InputController;
class ShortcutController;
class ShortcutModel;
class ShortcutView : public QWidget {
Q_OBJECT
@ -24,11 +26,9 @@ public:
ShortcutView(QWidget* parent = nullptr);
~ShortcutView();
void setModel(InputIndex* model);
void setController(ShortcutController* controller);
void setInputController(InputController* input);
const InputIndex* root() { return m_model.inputIndex(); }
protected:
virtual bool event(QEvent*) override;
virtual void closeEvent(QCloseEvent*) override;
@ -38,12 +38,12 @@ private slots:
void clear();
void updateButton(int button);
void updateAxis(int axis, int direction);
void updateGamepads();
private:
Ui::ShortcutView m_ui;
InputModel m_model;
ShortcutController* m_controller = nullptr;
ShortcutModel* m_model = nullptr;
InputController* m_input = nullptr;
};

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,10 @@
#include <functional>
#include <memory>
#include <mgba/core/core.h>
#include <mgba/core/thread.h>
#include "ActionMapper.h"
#include "InputController.h"
#include "LoadSaveState.h"
#include "LogController.h"
@ -37,6 +39,7 @@ class LogView;
class OverrideView;
class SensorView;
class ShaderSelector;
class ShortcutController;
class VideoView;
class WindowBackground;
@ -155,8 +158,9 @@ private:
template <typename T, typename... A> std::function<void()> openTView(A... arg);
template <typename T, typename... A> std::function<void()> openControllerTView(A... arg);
QAction* addControlledAction(QMenu* menu, QAction* action, const QString& name);
QAction* addHiddenAction(QMenu* menu, QAction* action, const QString& name);
Action* addGameAction(const QString& visibleName, const QString& name, Action::Function action, const QString& menu = {}, const QKeySequence& = {});
template<typename T, typename V> Action* addGameAction(const QString& visibleName, const QString& name, T* obj, V (T::*action)(), const QString& menu = {}, const QKeySequence& = {});
Action* addGameAction(const QString& visibleName, const QString& name, Action::BooleanFunction action, const QString& menu = {}, const QKeySequence& = {});
void updateTitle(float fps = -1);
@ -169,12 +173,15 @@ private:
std::unique_ptr<Display> m_display;
int m_savedScale;
// TODO: Move these to a new class
QList<QAction*> m_gameActions;
QList<QAction*> m_nonMpActions;
QList<QPair<QAction*, int>> m_platformActions;
QAction* m_multiWindow;
QMap<int, QAction*> m_frameSizes;
ActionMapper m_actions;
QList<Action*> m_gameActions;
QList<Action*> m_nonMpActions;
QMultiMap<mPlatform, Action*> m_platformActions;
Action* m_multiWindow;
QMap<int, Action*> m_frameSizes;
LogController m_log{0};
LogView* m_logView;
#ifdef USE_DEBUGGERS
@ -189,9 +196,7 @@ private:
QElapsedTimer m_frameTimer;
QTimer m_fpsTimer;
QList<QString> m_mruFiles;
QMenu* m_mruMenu = nullptr;
QMenu* m_videoLayers;
QMenu* m_audioChannels;
ShortcutController* m_shortcutController;
#if defined(BUILD_GL) || defined(BUILD_GLES2)
std::unique_ptr<ShaderSelector> m_shaderView;
#endif