Added keybind customization system

This commit is contained in:
=
2025-04-19 19:44:47 -07:00
parent bfb3ff8460
commit 7f5d1b86c7
12 changed files with 429 additions and 91 deletions

View File

@@ -137,6 +137,11 @@ add_library(ui STATIC
qt_joystickconfiguration.cpp
qt_joystickconfiguration.hpp
qt_joystickconfiguration.ui
qt_keybind.cpp
qt_keybind.hpp
qt_keybind.ui
qt_singlekeyseqedit.cpp
qt_singlekeyseqedit.hpp
qt_filefield.cpp
qt_filefield.hpp

View File

@@ -87,4 +87,4 @@ plat_vidapi_name(int api)
}
return name;
}
}

View File

@@ -139,6 +139,7 @@ namespace IOKit {
# include "be_keyboard.hpp"
extern MainWindow *main_window;
QShortcut *windowedShortcut;
filter_result
keyb_filter(BMessage *message, BHandler **target, BMessageFilter *filter)
@@ -676,9 +677,9 @@ MainWindow::MainWindow(QWidget *parent)
connect(new QShortcut(QKeySequence(Qt::SHIFT + Qt::Key_F10), this), &QShortcut::activated, this, [](){});
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
auto windowedShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_PageDown), this);
windowedShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_PageDown), this);
#else
auto windowedShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_PageDown), this);
windowedShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_PageDown), this);
#endif
windowedShortcut->setContext(Qt::ShortcutContext::ApplicationShortcut);
connect(windowedShortcut, &QShortcut::activated, this, [this] () {
@@ -761,6 +762,8 @@ MainWindow::MainWindow(QWidget *parent)
});
}
#endif
updateShortcuts();
}
void
@@ -826,6 +829,56 @@ MainWindow::closeEvent(QCloseEvent *event)
event->accept();
}
void MainWindow::updateShortcuts()
{
// Update menu shortcuts from accelerator table
// Note that the "Release mouse" shortcut is hardcoded elsewhere
// This section only applies to shortcuts anchored to UI elements
ui->actionTake_screenshot->setShortcut(QKeySequence());
ui->actionCtrl_Alt_Del->setShortcut(QKeySequence());
ui->actionCtrl_Alt_Esc->setShortcut(QKeySequence());
ui->actionFullscreen->setShortcut(QKeySequence());
ui->actionHard_Reset->setShortcut(QKeySequence());
int accID;
QKeySequence seq;
accID = FindAccelerator("screenshot");
seq = QKeySequence::fromString(acc_keys[accID].seq);
ui->actionTake_screenshot->setShortcut(seq);
accID = FindAccelerator("send_ctrl_alt_del");
seq = QKeySequence::fromString(acc_keys[accID].seq);
ui->actionCtrl_Alt_Del->setShortcut(seq);
accID = FindAccelerator("send_ctrl_alt_esc");
seq = QKeySequence::fromString(acc_keys[accID].seq);
ui->actionCtrl_Alt_Esc->setShortcut(seq);
accID = FindAccelerator("fullscreen");
seq = QKeySequence::fromString(acc_keys[accID].seq);
//printf("shortcut: %s\n", qPrintable(ui->actionFullscreen->shortcut().toString()));
ui->actionFullscreen->setShortcut(seq);
accID = FindAccelerator("hard_reset");
seq = QKeySequence::fromString(acc_keys[accID].seq);
ui->actionHard_Reset->setShortcut(seq);
// To rebind leave_fullscreen we have to disconnect the existing signal,
// build a new shortcut, then connect it.
accID = FindAccelerator("leave_fullscreen");
seq = QKeySequence::fromString(acc_keys[accID].seq);
disconnect(windowedShortcut,0,0,0);
windowedShortcut = new QShortcut(seq, this);
windowedShortcut->setContext(Qt::ShortcutContext::ApplicationShortcut);
connect(windowedShortcut, &QShortcut::activated, this, [this] () {
if (video_fullscreen)
ui->actionFullscreen->trigger();
});
}
void
MainWindow::resizeEvent(QResizeEvent *event)
{
@@ -1026,6 +1079,8 @@ MainWindow::on_actionSettings_triggered()
case QDialog::Accepted:
settings.save();
config_changed = 2;
printf("about to try\n");
updateShortcuts();
pc_reset_hard();
break;
case QDialog::Rejected:
@@ -1394,9 +1449,14 @@ MainWindow::keyPressEvent(QKeyEvent *event)
#endif
}
if (keyboard_ismsexit())
plat_mouse_capture(0);
// Check if mouse release combo has been entered
int accID = FindAccelerator("release_mouse");
QKeySequence seq = QKeySequence::fromString(acc_keys[accID].seq);
if (seq[0] == (event->key() | event->modifiers()))
plat_mouse_capture(0);
// TODO: Other accelerators should probably be here?
event->accept();
}

View File

@@ -5,6 +5,7 @@
#include <QLabel>
#include <QEvent>
#include <QFocusEvent>
#include <QShortcut>
#include <memory>
#include <array>
@@ -32,7 +33,8 @@ public:
QSize getRenderWidgetSize();
void setSendKeyboardInput(bool enabled);
void reloadAllRenderers();
QShortcut *windowedShortcut;
std::array<std::unique_ptr<RendererStack>, 8> renderers;
signals:
void paint(const QImage &image);
@@ -159,6 +161,7 @@ private:
std::unique_ptr<MachineStatus> status;
std::shared_ptr<MediaMenu> mm;
void updateShortcuts();
void processKeyboardInput(bool down, uint32_t keycode);
#ifdef Q_OS_MACOS
uint32_t last_modifiers = 0;
@@ -184,7 +187,6 @@ private:
friend class RendererStack; // For UI variable access by non-primary renderer windows.
friend class WindowsRawInputFilter; // Needed to reload renderers on style sheet changes.
bool isShowMessage = false;
};

View File

@@ -16,8 +16,11 @@
*/
#include "qt_settingsinput.hpp"
#include "ui_qt_settingsinput.h"
#include "qt_mainwindow.hpp"
#include <QDebug>
#include <QKeySequence>
#include <string>
extern "C" {
#include <86box/86box.h>
@@ -25,11 +28,18 @@ extern "C" {
#include <86box/machine.h>
#include <86box/mouse.h>
#include <86box/gameport.h>
#include <86box/ui.h>
}
#include "qt_models_common.hpp"
#include "qt_deviceconfig.hpp"
#include "qt_joystickconfiguration.hpp"
#include "qt_keybind.hpp"
extern MainWindow *main_window;
// Temporary working copy of key list
accelKey acc_keys_t[NUM_ACCELS];
SettingsInput::SettingsInput(QWidget *parent)
: QWidget(parent)
@@ -37,9 +47,55 @@ SettingsInput::SettingsInput(QWidget *parent)
{
ui->setupUi(this);
QStandardItemModel *model;
QStringList horizontalHeader;
QStringList verticalHeader;
horizontalHeader.append("Action");
horizontalHeader.append("Keybind");
QTableWidget *keyTable = ui->tableKeys;
keyTable->setRowCount(10);
keyTable->setColumnCount(3);
keyTable->setColumnHidden(2, true);
keyTable->setColumnWidth(0, 200);
keyTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
QStringList headers;
headers << "Action" << "Bound key";
keyTable->setHorizontalHeaderLabels(headers);
keyTable->verticalHeader()->setVisible(false);
keyTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
keyTable->setSelectionBehavior(QAbstractItemView::SelectRows);
keyTable->setSelectionMode(QAbstractItemView::SingleSelection);
keyTable->setShowGrid(true);
keyTable->setStyleSheet("QTableWidget::item:hover { }");
keyTable->setFocusPolicy(Qt::NoFocus);
keyTable->setSelectionMode(QAbstractItemView::NoSelection);
// Make a working copy of acc_keys so we can check for dupes later without getting
// confused
printf("Instantiating list\n");
for(int x=0;x<NUM_ACCELS;x++) {
strcpy(acc_keys_t[x].name, acc_keys[x].name);
strcpy(acc_keys_t[x].desc, acc_keys[x].desc);
strcpy(acc_keys_t[x].seq, acc_keys[x].seq);
}
refreshInputList();
connect(ui->tableKeys, &QTableWidget::cellDoubleClicked,
this, &SettingsInput::on_tableKeys_doubleClicked);
connect(ui->pushButtonBind, &QPushButton::clicked,
this, &SettingsInput::on_pushButtonBind_Clicked);
connect(ui->pushButtonClearBind, &QPushButton::clicked,
this, &SettingsInput::on_pushButtonClearBind_Clicked);
onCurrentMachineChanged(machine);
}
SettingsInput::~SettingsInput()
{
delete ui;
@@ -50,8 +106,15 @@ SettingsInput::save()
{
mouse_type = ui->comboBoxMouse->currentData().toInt();
joystick_type = ui->comboBoxJoystick->currentData().toInt();
// Copy accelerators from working set to global set
for(int x=0;x<NUM_ACCELS;x++) {
strcpy(acc_keys[x].name, acc_keys_t[x].name);
strcpy(acc_keys[x].desc, acc_keys_t[x].desc);
strcpy(acc_keys[x].seq, acc_keys_t[x].seq);
}
}
void
SettingsInput::onCurrentMachineChanged(int machineId)
{
@@ -105,6 +168,100 @@ SettingsInput::onCurrentMachineChanged(int machineId)
ui->comboBoxJoystick->setCurrentIndex(selectedRow);
}
void
SettingsInput::refreshInputList()
{
for (int x=0;x<NUM_ACCELS;x++) {
ui->tableKeys->setItem(x, 0, new QTableWidgetItem(acc_keys_t[x].desc));
ui->tableKeys->setItem(x, 1, new QTableWidgetItem(acc_keys_t[x].seq));
ui->tableKeys->setItem(x, 2, new QTableWidgetItem(acc_keys_t[x].name));
}
}
void
SettingsInput::on_tableKeys_currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn)
{
// Enable/disable bind/clear buttons if user clicked valid row
QTableWidgetItem *cell = ui->tableKeys->item(currentRow,1);
if (!cell)
{
ui->pushButtonBind->setEnabled(false);
ui->pushButtonClearBind->setEnabled(false);
}
else
{
ui->pushButtonBind->setEnabled(true);
ui->pushButtonClearBind->setEnabled(true);
}
}
void
SettingsInput::on_tableKeys_doubleClicked(int row, int col)
{
// Edit bind
QTableWidgetItem *cell = ui->tableKeys->item(row,1);
if (!cell) return;
QKeySequence keyseq = KeyBinder::BindKey(cell->text());
if (keyseq != false) {
// If no change was made, don't change anything.
if (keyseq.toString(QKeySequence::NativeText) == cell->text()) return;
// Otherwise, check for conflicts.
// Check against the *working* copy - NOT the one in use by the app,
// so we don't test against shortcuts the user already changed.
for(int x=0;x<NUM_ACCELS;x++)
{
if(QString::fromStdString(acc_keys_t[x].seq) == keyseq.toString(QKeySequence::NativeText))
{
// That key is already in use
main_window->showMessage(MBX_ANSI & MBX_INFO, "Bind conflict", "This key combo is already in use", false);
return;
}
}
// If we made it here, there were no conflicts.
// Go ahead and apply the bind.
// Find the correct accelerator key entry
int accKeyID = FindAccelerator(qPrintable(ui->tableKeys->item(row,2)->text()));
if (!accKeyID) return; // this should never happen
// Make the change
cell->setText(keyseq.toString(QKeySequence::NativeText));
strcpy(acc_keys_t[accKeyID].seq, qPrintable(keyseq.toString(QKeySequence::NativeText)));
refreshInputList();
}
}
void
SettingsInput::on_pushButtonBind_Clicked()
{
// Edit bind
QTableWidgetItem *cell = ui->tableKeys->currentItem();
if (!cell) return;
on_tableKeys_doubleClicked(cell->row(), cell->column());
}
void
SettingsInput::on_pushButtonClearBind_Clicked()
{
// Wipe bind
QTableWidgetItem *cell = ui->tableKeys->currentItem();
if (!cell) return;
cell->setText("");
// Find the correct accelerator key entry
int accKeyID = FindAccelerator(qPrintable(ui->tableKeys->item(cell->row(),2)->text()));
if (!accKeyID) return; // this should never happen
// Make the change
cell->setText("");
strcpy(acc_keys_t[accKeyID].seq, "");
}
void
SettingsInput::on_comboBoxMouse_currentIndexChanged(int index)
{

View File

@@ -2,6 +2,12 @@
#define QT_SETTINGSINPUT_HPP
#include <QWidget>
#include <QtGui/QStandardItemModel>
#include <QtGui/QStandardItem>
#include <QItemDelegate>
#include <QPainter>
#include <QVariant>
#include <QTableWidget>
namespace Ui {
class SettingsInput;
@@ -27,10 +33,15 @@ private slots:
void on_pushButtonJoystick2_clicked();
void on_pushButtonJoystick3_clicked();
void on_pushButtonJoystick4_clicked();
void on_tableKeys_doubleClicked(int row, int col);
void on_tableKeys_currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn);
void on_pushButtonBind_Clicked();
void on_pushButtonClearBind_Clicked();
private:
Ui::SettingsInput *ui;
int machineId = 0;
void refreshInputList();
};
#endif // QT_SETTINGSINPUT_HPP

View File

@@ -23,17 +23,16 @@
<property name="rightMargin">
<number>0</number>
</property>
<item row="2" column="1">
<widget class="QPushButton" name="pushButtonJoystick2">
<property name="text">
<string>Joystick 2...</string>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="comboBoxMouse">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Joystick:</string>
<property name="maxVisibleItems">
<number>30</number>
</property>
</widget>
</item>
@@ -44,13 +43,6 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Mouse:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="pushButtonJoystick3">
<property name="text">
@@ -58,21 +50,15 @@
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<item row="5" column="3">
<widget class="QPushButton" name="pushButtonBind">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
<property name="text">
<string>Bind</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="pushButtonJoystick1">
@@ -81,6 +67,44 @@
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="comboBoxJoystick">
<property name="maxVisibleItems">
<number>30</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Mouse:</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QPushButton" name="pushButtonClearBind">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Clear binding</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Joystick:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="pushButtonJoystick2">
<property name="text">
<string>Joystick 2...</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="pushButtonConfigureMouse">
<property name="sizePolicy">
@@ -94,23 +118,29 @@
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="comboBoxMouse">
<property name="maxVisibleItems">
<number>30</number>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Key Bindings:</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="comboBoxJoystick">
<property name="maxVisibleItems">
<number>30</number>
<item row="4" column="0" colspan="4">
<widget class="QTableWidget" name="tableKeys">
<property name="editTriggers">
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
</property>
<property name="tabKeyNavigation">
<bool>false</bool>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
</property>
</widget>
</item>