Refactor ActionShortcutEdit into MultiKeyEdit
This commit is contained in:
parent
43d3257d89
commit
f5964fbe7f
3 changed files with 227 additions and 0 deletions
50
include/ui/multikeyedit.h
Normal file
50
include/ui/multikeyedit.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef MULTIKEYEDIT_H
|
||||||
|
#define MULTIKEYEDIT_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QKeySequenceEdit>
|
||||||
|
|
||||||
|
class QLineEdit;
|
||||||
|
|
||||||
|
|
||||||
|
// A collection of QKeySequenceEdit's laid out horizontally.
|
||||||
|
class MultiKeyEdit : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MultiKeyEdit(QWidget *parent = nullptr, int fieldCount = 2);
|
||||||
|
|
||||||
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
|
||||||
|
int fieldCount() const;
|
||||||
|
void setFieldCount(int count);
|
||||||
|
QList<QKeySequence> keySequences() const;
|
||||||
|
bool removeOne(const QKeySequence &keySequence);
|
||||||
|
bool contains(const QKeySequence &keySequence) const;
|
||||||
|
void setContextMenuPolicy(Qt::ContextMenuPolicy policy);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void clear();
|
||||||
|
void setKeySequences(const QList<QKeySequence> &keySequences);
|
||||||
|
void addKeySequence(const QKeySequence &keySequence);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void keySequenceChanged(const QKeySequence &keySequence);
|
||||||
|
void editingFinished();
|
||||||
|
void customContextMenuRequested(const QPoint &pos);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<QKeySequenceEdit *> keySequenceEdit_vec;
|
||||||
|
QList<QKeySequence> keySequence_list; // Used to track changes
|
||||||
|
|
||||||
|
void addNewKeySequenceEdit();
|
||||||
|
void alignKeySequencesLeft();
|
||||||
|
void setFocusToLastNonEmptyKeySequenceEdit();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onEditingFinished();
|
||||||
|
void showDefaultContextMenu(QLineEdit *lineEdit, const QPoint &pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MULTIKEYEDIT_H
|
|
@ -73,6 +73,7 @@ SOURCES += src/core/block.cpp \
|
||||||
src/ui/mapruler.cpp \
|
src/ui/mapruler.cpp \
|
||||||
src/ui/shortcut.cpp \
|
src/ui/shortcut.cpp \
|
||||||
src/ui/shortcutseditor.cpp \
|
src/ui/shortcutseditor.cpp \
|
||||||
|
src/ui/multikeyedit.cpp \
|
||||||
src/config.cpp \
|
src/config.cpp \
|
||||||
src/editor.cpp \
|
src/editor.cpp \
|
||||||
src/main.cpp \
|
src/main.cpp \
|
||||||
|
@ -144,6 +145,7 @@ HEADERS += include/core/block.h \
|
||||||
include/ui/mapruler.h \
|
include/ui/mapruler.h \
|
||||||
include/ui/shortcut.h \
|
include/ui/shortcut.h \
|
||||||
include/ui/shortcutseditor.h \
|
include/ui/shortcutseditor.h \
|
||||||
|
include/ui/multikeyedit.h \
|
||||||
include/config.h \
|
include/config.h \
|
||||||
include/editor.h \
|
include/editor.h \
|
||||||
include/mainwindow.h \
|
include/mainwindow.h \
|
||||||
|
|
175
src/ui/multikeyedit.cpp
Normal file
175
src/ui/multikeyedit.cpp
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
#include "multikeyedit.h"
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QtEvents>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
|
|
||||||
|
MultiKeyEdit::MultiKeyEdit(QWidget *parent, int fieldCount) :
|
||||||
|
QWidget(parent),
|
||||||
|
keySequenceEdit_vec(QVector<QKeySequenceEdit *>()),
|
||||||
|
keySequence_list(QList<QKeySequence>())
|
||||||
|
{
|
||||||
|
setLayout(new QHBoxLayout(this));
|
||||||
|
layout()->setContentsMargins(0, 0, 0, 0);
|
||||||
|
setFieldCount(fieldCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MultiKeyEdit::eventFilter(QObject *watched, QEvent *event) {
|
||||||
|
if (event->type() == QEvent::KeyPress) {
|
||||||
|
auto *watched_kse = qobject_cast<QKeySequenceEdit *>(watched);
|
||||||
|
if (!watched_kse)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto *keyEvent = static_cast<QKeyEvent *>(event);
|
||||||
|
if (keyEvent->key() == Qt::Key_Escape) {
|
||||||
|
watched_kse->clearFocus();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
watched_kse->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->type() == QEvent::ContextMenu) {
|
||||||
|
auto *watched_lineEdit = qobject_cast<QLineEdit *>(watched);
|
||||||
|
if (!watched_lineEdit)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto *contextMenuEvent = static_cast<QContextMenuEvent *>(event);
|
||||||
|
if (contextMenuPolicy() == Qt::DefaultContextMenu) {
|
||||||
|
showDefaultContextMenu(watched_lineEdit, contextMenuEvent->pos());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MultiKeyEdit::fieldCount() const {
|
||||||
|
return keySequenceEdit_vec.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiKeyEdit::setFieldCount(int count) {
|
||||||
|
if (count < 1)
|
||||||
|
count = 1;
|
||||||
|
|
||||||
|
while (keySequenceEdit_vec.count() < count)
|
||||||
|
addNewKeySequenceEdit();
|
||||||
|
|
||||||
|
while (keySequenceEdit_vec.count() > count)
|
||||||
|
delete keySequenceEdit_vec.takeLast();
|
||||||
|
|
||||||
|
alignKeySequencesLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QKeySequence> MultiKeyEdit::keySequences() const {
|
||||||
|
QList<QKeySequence> current_keySequences;
|
||||||
|
for (auto *kse : keySequenceEdit_vec)
|
||||||
|
if (!kse->keySequence().isEmpty())
|
||||||
|
current_keySequences.append(kse->keySequence());
|
||||||
|
return current_keySequences;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MultiKeyEdit::removeOne(const QKeySequence &keySequence) {
|
||||||
|
for (auto *keySequenceEdit : keySequenceEdit_vec) {
|
||||||
|
if (keySequenceEdit->keySequence() == keySequence) {
|
||||||
|
keySequence_list.removeOne(keySequence);
|
||||||
|
keySequenceEdit->clear();
|
||||||
|
alignKeySequencesLeft();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MultiKeyEdit::contains(const QKeySequence &keySequence) const {
|
||||||
|
for (auto current_keySequence : keySequences())
|
||||||
|
if (current_keySequence == keySequence)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiKeyEdit::setContextMenuPolicy(Qt::ContextMenuPolicy policy) {
|
||||||
|
QWidget::setContextMenuPolicy(policy);
|
||||||
|
auto lineEdit_children = findChildren<QLineEdit *>();
|
||||||
|
for (auto *lineEdit : lineEdit_children)
|
||||||
|
lineEdit->setContextMenuPolicy(policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiKeyEdit::clear() {
|
||||||
|
for (auto *keySequenceEdit : keySequenceEdit_vec)
|
||||||
|
keySequenceEdit->clear();
|
||||||
|
keySequence_list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiKeyEdit::setKeySequences(const QList<QKeySequence> &keySequences) {
|
||||||
|
clear();
|
||||||
|
keySequence_list = keySequences;
|
||||||
|
int minCount = qMin(keySequenceEdit_vec.count(), keySequence_list.count());
|
||||||
|
for (int i = 0; i < minCount; ++i)
|
||||||
|
keySequenceEdit_vec[i]->setKeySequence(keySequence_list[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiKeyEdit::addKeySequence(const QKeySequence &keySequence) {
|
||||||
|
keySequenceEdit_vec.last()->setKeySequence(keySequence);
|
||||||
|
alignKeySequencesLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiKeyEdit::addNewKeySequenceEdit() {
|
||||||
|
auto *keySequenceEdit = new QKeySequenceEdit(this);
|
||||||
|
keySequenceEdit->installEventFilter(this);
|
||||||
|
connect(keySequenceEdit, &QKeySequenceEdit::editingFinished,
|
||||||
|
this, &MultiKeyEdit::onEditingFinished);
|
||||||
|
connect(keySequenceEdit, &QKeySequenceEdit::keySequenceChanged,
|
||||||
|
this, &MultiKeyEdit::keySequenceChanged);
|
||||||
|
|
||||||
|
auto *lineEdit = keySequenceEdit->findChild<QLineEdit *>();
|
||||||
|
lineEdit->installEventFilter(this);
|
||||||
|
connect(lineEdit, &QLineEdit::customContextMenuRequested,
|
||||||
|
this, &MultiKeyEdit::customContextMenuRequested);
|
||||||
|
|
||||||
|
layout()->addWidget(keySequenceEdit);
|
||||||
|
keySequenceEdit_vec.append(keySequenceEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift all key sequences left if there are any empty QKeySequenceEdit's.
|
||||||
|
void MultiKeyEdit::alignKeySequencesLeft() {
|
||||||
|
blockSignals(true);
|
||||||
|
setKeySequences(keySequences());
|
||||||
|
blockSignals(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiKeyEdit::setFocusToLastNonEmptyKeySequenceEdit() {
|
||||||
|
for (auto it = keySequenceEdit_vec.rbegin(); it != keySequenceEdit_vec.rend(); ++it) {
|
||||||
|
if (!(*it)->keySequence().isEmpty()) {
|
||||||
|
(*it)->setFocus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiKeyEdit::onEditingFinished() {
|
||||||
|
auto *keySequenceEdit = qobject_cast<QKeySequenceEdit *>(sender());
|
||||||
|
if (keySequenceEdit && keySequence_list.contains(keySequenceEdit->keySequence()))
|
||||||
|
removeOne(keySequenceEdit->keySequence());
|
||||||
|
alignKeySequencesLeft();
|
||||||
|
setFocusToLastNonEmptyKeySequenceEdit();
|
||||||
|
|
||||||
|
emit editingFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* QKeySequenceEdit doesn't send or receive context menu events, but it owns QLineEdit that does.
|
||||||
|
* This QLineEdit hijacks those events and so we need to filter/connect to it directly, rather than
|
||||||
|
* the QKeySequenceEdit. I wouldn't be surprised if Qt fixed this in the future, in which case any
|
||||||
|
* context menu related code in this class might need to change. */
|
||||||
|
void MultiKeyEdit::showDefaultContextMenu(QLineEdit *lineEdit, const QPoint &pos) {
|
||||||
|
QMenu menu(this);
|
||||||
|
QAction clearAction("Clear Shortcut", &menu);
|
||||||
|
connect(&clearAction, &QAction::triggered, lineEdit, [&lineEdit]() {
|
||||||
|
lineEdit->clear();
|
||||||
|
});
|
||||||
|
menu.addAction(&clearAction);
|
||||||
|
menu.exec(lineEdit->mapToGlobal(pos));
|
||||||
|
}
|
Loading…
Reference in a new issue