Qt: Start on key shortcut editor

This commit is contained in:
Jeffrey Pfau 2015-01-03 23:57:37 -08:00
parent bb78cc1bd3
commit 0ce8ca36fa
8 changed files with 493 additions and 30 deletions

View File

@ -50,6 +50,8 @@ set(SOURCE_FILES
LogView.cpp
SavestateButton.cpp
SettingsView.cpp
ShortcutController.cpp
ShortcutView.cpp
Window.cpp
VFileDevice.cpp
VideoView.cpp)
@ -60,6 +62,7 @@ qt5_wrap_ui(UI_FILES
LoadSaveState.ui
LogView.ui
SettingsView.ui
ShortcutView.ui
VideoView.ui)
set(QT_LIBRARIES)

View File

@ -0,0 +1,162 @@
/* 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 <QAction>
#include <QMenu>
using namespace QGBA;
ShortcutController::ShortcutController(QObject* parent)
: QAbstractItemModel(parent)
{
}
QVariant ShortcutController::data(const QModelIndex& index, int role) const {
if (role != Qt::DisplayRole || !index.isValid()) {
return QVariant();
}
const QModelIndex& parent = index.parent();
if (parent.isValid()) {
const ShortcutMenu& menu = m_menus[parent.row()];
const ShortcutItem& item = menu.shortcuts()[index.row()];
switch (index.column()) {
case 0:
return item.visibleName();
case 1:
return item.action()->shortcut().toString(QKeySequence::NativeText);
}
} else if (index.column() == 0) {
return m_menus[index.row()].visibleName();
}
return QVariant();
}
QVariant ShortcutController::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("Shortcut");
}
}
return section;
}
QModelIndex ShortcutController::index(int row, int column, const QModelIndex& parent) const {
if (!parent.isValid()) {
return createIndex(row, column, -1);
}
return createIndex(row, column, parent.row());
}
QModelIndex ShortcutController::parent(const QModelIndex& index) const {
if (!index.isValid()) {
return QModelIndex();
}
if (index.internalId() == -1) {
return QModelIndex();
}
return createIndex(index.internalId(), 0, -1);
}
int ShortcutController::columnCount(const QModelIndex& index) const {
return 2;
}
int ShortcutController::rowCount(const QModelIndex& index) const {
if (index.parent().isValid()) {
return 0;
}
if (index.isValid()) {
return m_menus[index.row()].shortcuts().count();
}
return m_menus.count();
}
void ShortcutController::addAction(QMenu* menu, QAction* action, const QString& name) {
ShortcutMenu* smenu = nullptr;
int row = 0;
for (auto iter = m_menus.end(); iter-- != m_menus.begin(); ++row) {
if (iter->menu() == menu) {
smenu = &(*iter);
break;
}
}
if (!smenu) {
return;
}
QModelIndex parent = createIndex(row, 0, -1);
beginInsertRows(parent, smenu->shortcuts().count(), smenu->shortcuts().count());
smenu->addAction(action, name);
endInsertRows();
emit dataChanged(createIndex(smenu->shortcuts().count() - 1, 0, row), createIndex(smenu->shortcuts().count() - 1, 1, row));
}
void ShortcutController::addMenu(QMenu* menu) {
beginInsertRows(QModelIndex(), m_menus.count(), m_menus.count());
m_menus.append(ShortcutMenu(menu));
endInsertRows();
emit dataChanged(createIndex(m_menus.count() - 1, 0, -1), createIndex(m_menus.count() - 1, 0, -1));
}
const QAction* ShortcutController::actionAt(const QModelIndex& index) const {
if (!index.isValid()) {
return nullptr;
}
const QModelIndex& parent = index.parent();
if (!parent.isValid()) {
return nullptr;
}
if (parent.row() > m_menus.count()) {
return nullptr;
}
const ShortcutMenu& menu = m_menus[parent.row()];
if (index.row() > menu.shortcuts().count()) {
return nullptr;
}
const ShortcutItem& item = menu.shortcuts()[index.row()];
return item.action();
}
void ShortcutController::updateKey(const QModelIndex& index, const QKeySequence& keySequence) {
if (!index.isValid()) {
return;
}
const QModelIndex& parent = index.parent();
if (!parent.isValid()) {
return;
}
ShortcutMenu& menu = m_menus[parent.row()];
ShortcutItem& item = menu.shortcuts()[index.row()];
item.action()->setShortcut(keySequence);
emit dataChanged(createIndex(index.row(), 0, index.internalId()), createIndex(index.row(), 1, index.internalId()));
}
ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& name)
: m_action(action)
, m_name(name)
{
m_visibleName = action->text()
.remove(QRegExp("&(?!&)"))
.remove("...");
}
ShortcutController::ShortcutMenu::ShortcutMenu(QMenu* menu)
: m_menu(menu)
{
m_visibleName = menu->title()
.remove(QRegExp("&(?!&)"))
.remove("...");
}
void ShortcutController::ShortcutMenu::addAction(QAction* action, const QString& name) {
m_shortcuts.append(ShortcutItem(action, name));
}

View File

@ -0,0 +1,74 @@
/* 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/. */
#ifndef QGBA_SHORTCUT_MODEL
#define QGBA_SHORTCUT_MODEL
#include <QAbstractItemModel>
class QAction;
class QMenu;
class QString;
namespace QGBA {
class ShortcutController : public QAbstractItemModel {
public:
ShortcutController(QObject* parent = nullptr);
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) 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 addMenu(QMenu* menu);
const QAction* actionAt(const QModelIndex& index) const;
void updateKey(const QModelIndex& index, const QKeySequence& keySequence);
private:
class ShortcutItem {
public:
ShortcutItem(QAction* action, const QString& name);
QAction* action() { return m_action; }
const QAction* action() const { return m_action; }
const QString& visibleName() const { return m_visibleName; }
const QString& name() const { return m_name; }
private:
QAction* m_action;
QString m_name;
QString m_visibleName;
};
class ShortcutMenu {
public:
ShortcutMenu(QMenu* action);
QMenu* menu() { return m_menu; }
const QMenu* menu() const { return m_menu; }
const QString& visibleName() const { return m_visibleName; }
QList<ShortcutItem>& shortcuts() { return m_shortcuts; }
const QList<ShortcutItem>& shortcuts() const { return m_shortcuts; }
void addAction(QAction* action, const QString& name);
private:
QMenu* m_menu;
QString m_visibleName;
QList<ShortcutItem> m_shortcuts;
};
QList<ShortcutMenu> m_menus;
};
}
#endif

View File

@ -0,0 +1,44 @@
/* 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 "ShortcutView.h"
#include "ShortcutController.h"
using namespace QGBA;
ShortcutView::ShortcutView(QWidget* parent)
: QWidget(parent)
{
m_ui.setupUi(this);
connect(m_ui.keySequenceEdit, SIGNAL(editingFinished()), this, SLOT(updateKey()));
connect(m_ui.shortcutTable, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(loadKey(const QModelIndex&)));
}
void ShortcutView::setController(ShortcutController* controller) {
m_controller = controller;
m_ui.shortcutTable->setModel(controller);
}
void ShortcutView::loadKey(const QModelIndex& index) {
if (!m_controller) {
return;
}
const QAction* action = m_controller->actionAt(index);
if (!action) {
return;
}
m_ui.keySequenceEdit->setFocus();
m_ui.keySequenceEdit->setKeySequence(action->shortcut());
}
void ShortcutView::updateKey() {
if (!m_controller) {
return;
}
m_ui.keySequenceEdit->clearFocus();
m_controller->updateKey(m_ui.shortcutTable->selectionModel()->currentIndex(), m_ui.keySequenceEdit->keySequence());
}

View File

@ -0,0 +1,37 @@
/* 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/. */
#ifndef QGBA_SHORTCUT_VIEW
#define QGBA_SHORTCUT_VIEW
#include <QWidget>
#include "ui_ShortcutView.h"
namespace QGBA {
class ShortcutController;
class ShortcutView : public QWidget {
Q_OBJECT
public:
ShortcutView(QWidget* parent = nullptr);
void setController(ShortcutController* controller);
private slots:
void loadKey(const QModelIndex&);
void updateKey();
private:
Ui::ShortcutView m_ui;
ShortcutController* m_controller;
};
}
#endif

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ShortcutView</class>
<widget class="QWidget" name="ShortcutView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Shortcuts</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeView" name="shortcutTable">
<attribute name="headerDefaultSectionSize">
<number>120</number>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QKeySequenceEdit" name="keySequenceEdit"/>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QRadioButton" name="radioButton">
<property name="text">
<string>Keyboard</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QRadioButton" name="radioButton_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Gamepad</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -22,6 +22,8 @@
#include "LoadSaveState.h"
#include "LogView.h"
#include "SettingsView.h"
#include "ShortcutController.h"
#include "ShortcutView.h"
#include "VideoView.h"
extern "C" {
@ -47,6 +49,7 @@ Window::Window(ConfigController* config, QWidget* parent)
, m_gdbController(nullptr)
#endif
, m_mruMenu(nullptr)
, m_shortcutController(new ShortcutController(this))
{
setWindowTitle(PROJECT_NAME);
setFocusPolicy(Qt::StrongFocus);
@ -207,6 +210,14 @@ void Window::openSettingsWindow() {
settingsWindow->show();
}
void Window::openShortcutWindow() {
ShortcutView* shortcutView = new ShortcutView();
shortcutView->setController(m_shortcutController);
connect(this, SIGNAL(shutdown()), shortcutView, SLOT(close()));
shortcutView->setAttribute(Qt::WA_DeleteOnClose);
shortcutView->show();
}
void Window::openGamePakWindow() {
GamePakView* gamePakWindow = new GamePakView(m_controller);
connect(this, SIGNAL(shutdown()), gamePakWindow, SLOT(close()));
@ -416,9 +427,10 @@ void Window::openStateWindow(LoadSave ls) {
void Window::setupMenu(QMenuBar* menubar) {
menubar->clear();
QMenu* fileMenu = menubar->addMenu(tr("&File"));
addAction(fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open));
fileMenu->addAction(tr("Load &BIOS..."), this, SLOT(selectBIOS()));
fileMenu->addAction(tr("Load &patch..."), this, SLOT(selectPatch()));
m_shortcutController->addMenu(fileMenu);
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open), "loadROM");
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &BIOS..."), this, SLOT(selectBIOS())), "loadBIOS");
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &patch..."), this, SLOT(selectPatch())), "loadPatch");
m_mruMenu = fileMenu->addMenu(tr("Recent"));
@ -428,15 +440,13 @@ void Window::setupMenu(QMenuBar* menubar) {
loadState->setShortcut(tr("F10"));
connect(loadState, &QAction::triggered, [this]() { this->openStateWindow(LoadSave::LOAD); });
m_gameActions.append(loadState);
addAction(loadState);
fileMenu->addAction(loadState);
addControlledAction(fileMenu, loadState, "loadState");
QAction* saveState = new QAction(tr("&Save state"), fileMenu);
saveState->setShortcut(tr("Shift+F10"));
connect(saveState, &QAction::triggered, [this]() { this->openStateWindow(LoadSave::SAVE); });
m_gameActions.append(saveState);
addAction(saveState);
fileMenu->addAction(saveState);
addControlledAction(fileMenu, saveState, "saveState");
QMenu* quickLoadMenu = fileMenu->addMenu(tr("Quick load"));
QMenu* quickSaveMenu = fileMenu->addMenu(tr("Quick save"));
@ -459,21 +469,21 @@ void Window::setupMenu(QMenuBar* menubar) {
#ifndef Q_OS_MAC
fileMenu->addSeparator();
fileMenu->addAction(tr("E&xit"), this, SLOT(close()), QKeySequence::Quit);
addControlledAction(fileMenu, fileMenu->addAction(tr("E&xit"), this, SLOT(close()), QKeySequence::Quit), "quit");
#endif
QMenu* emulationMenu = menubar->addMenu(tr("&Emulation"));
m_shortcutController->addMenu(emulationMenu);
QAction* reset = new QAction(tr("&Reset"), emulationMenu);
reset->setShortcut(tr("Ctrl+R"));
connect(reset, SIGNAL(triggered()), m_controller, SLOT(reset()));
m_gameActions.append(reset);
addAction(reset);
emulationMenu->addAction(reset);
addControlledAction(emulationMenu, reset, "reset");
QAction* shutdown = new QAction(tr("Sh&utdown"), emulationMenu);
connect(shutdown, SIGNAL(triggered()), m_controller, SLOT(closeGame()));
m_gameActions.append(shutdown);
emulationMenu->addAction(shutdown);
addControlledAction(emulationMenu, shutdown, "shutdown");
emulationMenu->addSeparator();
QAction* pause = new QAction(tr("&Pause"), emulationMenu);
@ -491,15 +501,13 @@ void Window::setupMenu(QMenuBar* menubar) {
});
connect(m_controller, &GameController::gameUnpaused, [pause]() { pause->setChecked(false); });
m_gameActions.append(pause);
addAction(pause);
emulationMenu->addAction(pause);
addControlledAction(emulationMenu, pause, "pause");
QAction* frameAdvance = new QAction(tr("&Next frame"), emulationMenu);
frameAdvance->setShortcut(tr("Ctrl+N"));
connect(frameAdvance, SIGNAL(triggered()), m_controller, SLOT(frameAdvance()));
m_gameActions.append(frameAdvance);
addAction(frameAdvance);
emulationMenu->addAction(frameAdvance);
addControlledAction(emulationMenu, frameAdvance, "frameAdvance");
emulationMenu->addSeparator();
@ -508,8 +516,7 @@ void Window::setupMenu(QMenuBar* menubar) {
turbo->setChecked(false);
turbo->setShortcut(tr("Shift+Tab"));
connect(turbo, SIGNAL(triggered(bool)), m_controller, SLOT(setTurbo(bool)));
addAction(turbo);
emulationMenu->addAction(turbo);
addControlledAction(emulationMenu, turbo, "fastForward");
ConfigOption* videoSync = m_config->addOption("videoSync");
videoSync->addBoolean(tr("Sync to &video"), emulationMenu);
@ -522,6 +529,7 @@ void Window::setupMenu(QMenuBar* menubar) {
m_config->updateOption("audioSync");
QMenu* avMenu = menubar->addMenu(tr("Audio/&Video"));
m_shortcutController->addMenu(avMenu);
QMenu* frameMenu = avMenu->addMenu(tr("Frame size"));
for (int i = 1; i <= 6; ++i) {
QAction* setSize = new QAction(tr("%1x").arg(QString::number(i)), avMenu);
@ -531,7 +539,7 @@ void Window::setupMenu(QMenuBar* menubar) {
});
frameMenu->addAction(setSize);
}
addAction(frameMenu->addAction(tr("Fullscreen"), this, SLOT(toggleFullScreen()), QKeySequence("Ctrl+F")));
addControlledAction(frameMenu, frameMenu->addAction(tr("Fullscreen"), this, SLOT(toggleFullScreen()), QKeySequence("Ctrl+F")), "fullscreen");
ConfigOption* lockAspectRatio = m_config->addOption("lockAspectRatio");
lockAspectRatio->addBoolean(tr("Lock aspect ratio"), avMenu);
@ -586,52 +594,51 @@ void Window::setupMenu(QMenuBar* menubar) {
screenshot->setShortcut(tr("F12"));
connect(screenshot, SIGNAL(triggered()), m_display, SLOT(screenshot()));
m_gameActions.append(screenshot);
addAction(screenshot);
avMenu->addAction(screenshot);
addControlledAction(avMenu, screenshot, "screenshot");
#endif
#ifdef USE_FFMPEG
QAction* recordOutput = new QAction(tr("Record output..."), avMenu);
recordOutput->setShortcut(tr("F11"));
connect(recordOutput, SIGNAL(triggered()), this, SLOT(openVideoWindow()));
addAction(recordOutput);
avMenu->addAction(recordOutput);
addControlledAction(avMenu, recordOutput, "recordOutput");
#endif
#ifdef USE_MAGICK
QAction* recordGIF = new QAction(tr("Record GIF..."), avMenu);
recordGIF->setShortcut(tr("Shift+F11"));
connect(recordGIF, SIGNAL(triggered()), this, SLOT(openGIFWindow()));
addAction(recordGIF);
avMenu->addAction(recordGIF);
addControlledAction(avMenu, recordGIF, "recordGIF");
#endif
QMenu* toolsMenu = menubar->addMenu(tr("&Tools"));
m_shortcutController->addMenu(toolsMenu);
QAction* viewLogs = new QAction(tr("View &logs..."), toolsMenu);
connect(viewLogs, SIGNAL(triggered()), m_logView, SLOT(show()));
toolsMenu->addAction(viewLogs);
addControlledAction(toolsMenu, viewLogs, "viewLogs");
QAction* gamePak = new QAction(tr("Game &Pak overrides..."), toolsMenu);
connect(gamePak, SIGNAL(triggered()), this, SLOT(openGamePakWindow()));
toolsMenu->addAction(gamePak);
addControlledAction(toolsMenu, gamePak, "gamePakOverrides");
#ifdef USE_GDB_STUB
QAction* gdbWindow = new QAction(tr("Start &GDB server..."), toolsMenu);
connect(gdbWindow, SIGNAL(triggered()), this, SLOT(gdbOpen()));
toolsMenu->addAction(gdbWindow);
addControlledAction(toolsMenu, gdbWindow, "gdbWindow");
#endif
toolsMenu->addSeparator();
toolsMenu->addAction(tr("Settings"), this, SLOT(openSettingsWindow()));
addControlledAction(toolsMenu, toolsMenu->addAction(tr("Settings..."), this, SLOT(openSettingsWindow())), "settings");
addControlledAction(toolsMenu, toolsMenu->addAction(tr("Edit shortcuts..."), this, SLOT(openShortcutWindow())), "shortcuts");
QAction* keymap = new QAction(tr("Remap keyboard..."), toolsMenu);
connect(keymap, SIGNAL(triggered()), this, SLOT(openKeymapWindow()));
toolsMenu->addAction(keymap);
addControlledAction(toolsMenu, keymap, "remapKeyboard");
#ifdef BUILD_SDL
QAction* gamepad = new QAction(tr("Remap gamepad..."), toolsMenu);
connect(gamepad, SIGNAL(triggered()), this, SLOT(openGamepadWindow()));
toolsMenu->addAction(gamepad);
addControlledAction(toolsMenu, gamepad, "remapGamepad");
#endif
ConfigOption* skipBios = m_config->addOption("skipBios");
@ -681,6 +688,13 @@ void Window::updateMRU() {
m_mruMenu->setEnabled(i > 0);
}
QAction* Window::addControlledAction(QMenu* menu, QAction* action, const QString& name) {
m_shortcutController->addAction(menu, action, name);
menu->addAction(action);
addAction(action);
return action;
}
WindowBackground::WindowBackground(QWidget* parent)
: QLabel(parent)
{

View File

@ -29,6 +29,7 @@ class ConfigController;
class GameController;
class GIFView;
class LogView;
class ShortcutController;
class VideoView;
class WindowBackground;
@ -62,6 +63,7 @@ public slots:
void openKeymapWindow();
void openSettingsWindow();
void openShortcutWindow();
void openGamePakWindow();
@ -110,6 +112,8 @@ private:
void appendMRU(const QString& fname);
void updateMRU();
QAction* addControlledAction(QMenu* menu, QAction* action, const QString& name);
GameController* m_controller;
Display* m_display;
QList<QAction*> m_gameActions;
@ -123,6 +127,7 @@ private:
QTimer m_fpsTimer;
QList<QString> m_mruFiles;
QMenu* m_mruMenu;
ShortcutController* m_shortcutController;
#ifdef USE_FFMPEG
VideoView* m_videoView;