Redesign ShortcutsEditor to take an obj list and refactor config to reflect that
This commit is contained in:
parent
f5964fbe7f
commit
2fb3bf4e26
8 changed files with 356 additions and 365 deletions
|
@ -1,21 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ShortcutsEditor</class>
|
||||
<widget class="QDialog" name="ShortcutsEditor">
|
||||
<widget class="QMainWindow" name="ShortcutsEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>540</width>
|
||||
<height>640</height>
|
||||
<width>800</width>
|
||||
<height>700</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Shortcuts Editor</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea_Shortcuts">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
@ -24,13 +52,22 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>516</width>
|
||||
<height>580</height>
|
||||
<width>794</width>
|
||||
<height>642</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Box</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
|
@ -43,6 +80,10 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
|
@ -201,53 +201,49 @@ class ShortcutsConfig : public KeyValueConfigBase
|
|||
{
|
||||
public:
|
||||
ShortcutsConfig() :
|
||||
userShortcuts(QMultiMap<QString, QKeySequence>()),
|
||||
defaultShortcuts(QMultiMap<QString, QKeySequence>())
|
||||
user_shortcuts({ }),
|
||||
default_shortcuts({ })
|
||||
{ }
|
||||
|
||||
virtual void reset() override { userShortcuts.clear(); }
|
||||
virtual void reset() override { user_shortcuts.clear(); }
|
||||
|
||||
void setDefaultShortcuts(const QList<QAction *> &actions);
|
||||
void setDefaultShortcuts(const QList<Shortcut *> &shortcuts);
|
||||
void setDefaultShortcuts(const QList<QAction *> &actions, const QList<Shortcut *> &shortcuts);
|
||||
QList<QKeySequence> getDefaultShortcuts(QAction *action) const;
|
||||
QList<QKeySequence> getDefaultShortcuts(Shortcut *shortcut) const;
|
||||
void setDefaultShortcuts(const QObjectList &objects);
|
||||
void setDefaultShortcuts(const QMultiMap<const QObject *, QKeySequence> &objects_keySequences);
|
||||
QList<QKeySequence> defaultShortcuts(const QObject *object) const;
|
||||
|
||||
void setUserShortcuts(const QList<QAction *> &actions);
|
||||
void setUserShortcuts(const QList<Shortcut *> &shortcuts);
|
||||
void setUserShortcuts(const QList<QAction *> &actions, const QList<Shortcut *> &shortcuts);
|
||||
QList<QKeySequence> getUserShortcuts(QAction *action) const;
|
||||
QList<QKeySequence> getUserShortcuts(Shortcut *shortcut) const;
|
||||
void setUserShortcuts(const QObjectList &objects);
|
||||
void setUserShortcuts(const QMultiMap<const QObject *, QKeySequence> &objects_keySequences);
|
||||
QList<QKeySequence> userShortcuts(const QObject *object) const;
|
||||
|
||||
static bool objectNameIsValid(const QObject *object);
|
||||
|
||||
protected:
|
||||
virtual QString getConfigFilepath() override;
|
||||
virtual void parseConfigKeyValue(QString key, QString value) override;
|
||||
virtual QMap<QString, QString> getKeyValueMap() override;
|
||||
virtual void onNewConfigFileCreated() override {};
|
||||
virtual void setUnreadKeys() override {};
|
||||
virtual void onNewConfigFileCreated() override { };
|
||||
virtual void setUnreadKeys() override { };
|
||||
|
||||
private:
|
||||
QMultiMap<QString, QKeySequence> userShortcuts;
|
||||
QMultiMap<QString, QKeySequence> defaultShortcuts;
|
||||
QMultiMap<QString, QKeySequence> user_shortcuts;
|
||||
QMultiMap<QString, QKeySequence> default_shortcuts;
|
||||
|
||||
enum StoreType {
|
||||
User,
|
||||
Default
|
||||
};
|
||||
|
||||
QString cfgKey(const QObject *object) const;
|
||||
QList<QKeySequence> currentShortcuts(const QObject *object) const;
|
||||
|
||||
void storeShortcutsFromList(StoreType storeType, const QObjectList &objects);
|
||||
void storeShortcuts(
|
||||
StoreType storeType,
|
||||
const QList<QAction *> &actions);
|
||||
void storeShortcuts(
|
||||
StoreType storeType,
|
||||
const QList<Shortcut *> &shortcuts);
|
||||
void storeShortcut(
|
||||
StoreType storeType,
|
||||
const QString &cfgKey,
|
||||
const QList<QKeySequence> &keySequences);
|
||||
QString cfgKey(QObject *object) const;
|
||||
};
|
||||
|
||||
// Call setDefaultShortcuts() prior to applying user shortcuts.
|
||||
extern ShortcutsConfig shortcutsConfig;
|
||||
|
||||
#endif // CONFIG_H
|
||||
|
|
|
@ -129,6 +129,7 @@ private slots:
|
|||
void openNewMapPopupWindow(int, QVariant);
|
||||
void onNewMapCreated();
|
||||
void onMapCacheCleared();
|
||||
void applyUserShortcuts();
|
||||
|
||||
void on_action_NewMap_triggered();
|
||||
void on_actionNew_Tileset_triggered();
|
||||
|
@ -292,12 +293,12 @@ private:
|
|||
|
||||
void initWindow();
|
||||
void initCustomUI();
|
||||
void initExtraShortcuts();
|
||||
void initExtraSignals();
|
||||
void initEditor();
|
||||
void initMiscHeapObjects();
|
||||
void initMapSortOrder();
|
||||
void initUserShortcuts();
|
||||
void initShortcuts();
|
||||
void initExtraShortcuts();
|
||||
void setProjectSpecificUIVisibility();
|
||||
void loadUserSettings();
|
||||
void applyMapListFilter(QString filterText);
|
||||
|
@ -312,6 +313,8 @@ private:
|
|||
bool isProjectOpen();
|
||||
void showExportMapImageWindow(bool stitchMode);
|
||||
void redrawMetatileSelection();
|
||||
|
||||
QObjectList shortcutableObjects() const;
|
||||
};
|
||||
|
||||
enum MapListUserRoles {
|
||||
|
|
|
@ -6,11 +6,17 @@
|
|||
#include <QShortcut>
|
||||
|
||||
|
||||
// Alternative to QShortcut that adds support for multiple QKeySequences.
|
||||
// Alternative to QShortcut that adds support for multiple key sequences.
|
||||
// Use this to allow the shortcut to be editable in ShortcutsEditor.
|
||||
class Shortcut : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DECLARE_PRIVATE(QShortcut)
|
||||
Q_PROPERTY(QKeySequence key READ key WRITE setKey)
|
||||
Q_PROPERTY(QString whatsThis READ whatsThis WRITE setWhatsThis)
|
||||
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
|
||||
Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat)
|
||||
Q_PROPERTY(Qt::ShortcutContext context READ context WRITE setContext)
|
||||
|
||||
public:
|
||||
explicit Shortcut(QWidget *parent);
|
||||
|
|
|
@ -1,80 +1,57 @@
|
|||
#ifndef SHORTCUTSEDITOR_H
|
||||
#define SHORTCUTSEDITOR_H
|
||||
|
||||
#include "shortcut.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QDialog>
|
||||
#include <QMap>
|
||||
#include <QHash>
|
||||
#include <QAction>
|
||||
|
||||
class QFormLayout;
|
||||
class MultiKeyEdit;
|
||||
class QAbstractButton;
|
||||
class QAction;
|
||||
class QKeySequenceEdit;
|
||||
class ActionShortcutEdit;
|
||||
|
||||
|
||||
namespace Ui {
|
||||
class ShortcutsEditor;
|
||||
}
|
||||
|
||||
class ShortcutsEditor : public QDialog
|
||||
class ShortcutsEditor : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ShortcutsEditor(QWidget *parent = nullptr);
|
||||
explicit ShortcutsEditor(const QObjectList &objectList, QWidget *parent = nullptr);
|
||||
~ShortcutsEditor();
|
||||
|
||||
signals:
|
||||
void shortcutsSaved();
|
||||
|
||||
private:
|
||||
Ui::ShortcutsEditor *ui;
|
||||
QMap<QString, QAction *> actions;
|
||||
QWidget *ase_container;
|
||||
QWidget *main_container;
|
||||
QMultiMap<QString, const QObject *> labels_objects;
|
||||
QHash<QString, QFormLayout *> contexts_layouts;
|
||||
QHash<MultiKeyEdit *, const QObject *> multiKeyEdits_objects;
|
||||
|
||||
void populateShortcuts();
|
||||
void parseObjectList(const QObjectList &objectList);
|
||||
QString getLabel(const QObject *object) const;
|
||||
bool stringPropertyIsNotEmpty(const QObject *object, const char *name) const;
|
||||
void populateMainContainer();
|
||||
QString getShortcutContext(const QObject *object) const;
|
||||
void addNewContextGroup(const QString &shortcutContext);
|
||||
void addNewMultiKeyEdit(const QObject *object, const QString &shortcutContext);
|
||||
QList<MultiKeyEdit *> siblings(MultiKeyEdit *multiKeyEdit) const;
|
||||
void promptUserOnDuplicateFound(MultiKeyEdit *current, MultiKeyEdit *sender);
|
||||
void removeKeySequence(const QKeySequence &keySequence, MultiKeyEdit *multiKeyEdit);
|
||||
void saveShortcuts();
|
||||
void resetShortcuts();
|
||||
void promptUser(ActionShortcutEdit *current, ActionShortcutEdit *sender);
|
||||
|
||||
private slots:
|
||||
void checkForDuplicates();
|
||||
void checkForDuplicates(const QKeySequence &keySequence);
|
||||
void dialogButtonClicked(QAbstractButton *button);
|
||||
};
|
||||
|
||||
|
||||
// A collection of QKeySequenceEdit's in a QHBoxLayout with a cooresponding QAction
|
||||
class ActionShortcutEdit : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ActionShortcutEdit(QWidget *parent = nullptr, QAction *action = nullptr, int count = 1);
|
||||
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
int count() const { return kse_children.count(); }
|
||||
void setCount(int count);
|
||||
QList<QKeySequence> shortcuts() const;
|
||||
void setShortcuts(const QList<QKeySequence> &keySequences);
|
||||
void applyShortcuts();
|
||||
bool removeOne(const QKeySequence &keySequence);
|
||||
bool contains(const QKeySequence &keySequence);
|
||||
bool contains(QKeySequenceEdit *keySequenceEdit);
|
||||
QKeySequence last() const { return shortcuts().last(); }
|
||||
|
||||
QAction *action;
|
||||
|
||||
public slots:
|
||||
void clear();
|
||||
|
||||
signals:
|
||||
void editingFinished();
|
||||
|
||||
private:
|
||||
QVector<QKeySequenceEdit *> kse_children;
|
||||
QList<QKeySequence> ks_list;
|
||||
|
||||
void updateShortcuts() { setShortcuts(shortcuts()); }
|
||||
void focusLast();
|
||||
|
||||
private slots:
|
||||
void onEditingFinished();
|
||||
};
|
||||
|
||||
#endif // SHORTCUTSEDITOR_H
|
||||
|
|
121
src/config.cpp
121
src/config.cpp
|
@ -13,6 +13,7 @@
|
|||
#include <QRegularExpression>
|
||||
#include <QStandardPaths>
|
||||
#include <QAction>
|
||||
#include <QAbstractButton>
|
||||
|
||||
KeyValueConfigBase::~KeyValueConfigBase() {
|
||||
|
||||
|
@ -722,13 +723,13 @@ QString ShortcutsConfig::getConfigFilepath() {
|
|||
void ShortcutsConfig::parseConfigKeyValue(QString key, QString value) {
|
||||
QStringList keySequences = value.split(' ');
|
||||
for (auto keySequence : keySequences)
|
||||
userShortcuts.insert(key, keySequence);
|
||||
user_shortcuts.insert(key, keySequence);
|
||||
}
|
||||
|
||||
QMap<QString, QString> ShortcutsConfig::getKeyValueMap() {
|
||||
QMap<QString, QString> map;
|
||||
for (auto cfg_key : userShortcuts.uniqueKeys()) {
|
||||
auto keySequences = userShortcuts.values(cfg_key);
|
||||
for (auto cfg_key : user_shortcuts.uniqueKeys()) {
|
||||
auto keySequences = user_shortcuts.values(cfg_key);
|
||||
QStringList values;
|
||||
for (auto keySequence : keySequences)
|
||||
values.append(keySequence.toString());
|
||||
|
@ -738,99 +739,77 @@ QMap<QString, QString> ShortcutsConfig::getKeyValueMap() {
|
|||
return map;
|
||||
}
|
||||
|
||||
// Call this before applying user shortcuts to be able to restore default shortcuts.
|
||||
void ShortcutsConfig::setDefaultShortcuts(const QList<QAction *> &actions) {
|
||||
storeShortcuts(StoreType::Default, actions);
|
||||
void ShortcutsConfig::setDefaultShortcuts(const QObjectList &objects) {
|
||||
storeShortcutsFromList(StoreType::Default, objects);
|
||||
save();
|
||||
}
|
||||
|
||||
// Call this before applying user shortcuts to be able to restore default shortcuts.
|
||||
void ShortcutsConfig::setDefaultShortcuts(const QList<Shortcut *> &shortcuts) {
|
||||
storeShortcuts(StoreType::Default, shortcuts);
|
||||
void ShortcutsConfig::setDefaultShortcuts(const QMultiMap<const QObject *, QKeySequence> &objects_keySequences) {
|
||||
for (auto *object : objects_keySequences.uniqueKeys())
|
||||
storeShortcuts(StoreType::Default, cfgKey(object), objects_keySequences.values(object));
|
||||
save();
|
||||
}
|
||||
|
||||
// Call this before applying user shortcuts to be able to restore default shortcuts.
|
||||
void ShortcutsConfig::setDefaultShortcuts(const QList<QAction *> &actions, const QList<Shortcut *> &shortcuts) {
|
||||
storeShortcuts(StoreType::Default, actions);
|
||||
storeShortcuts(StoreType::Default, shortcuts);
|
||||
QList<QKeySequence> ShortcutsConfig::defaultShortcuts(const QObject *object) const {
|
||||
return default_shortcuts.values(cfgKey(object));
|
||||
}
|
||||
|
||||
void ShortcutsConfig::setUserShortcuts(const QObjectList &objects) {
|
||||
storeShortcutsFromList(StoreType::User, objects);
|
||||
save();
|
||||
}
|
||||
|
||||
QList<QKeySequence> ShortcutsConfig::getDefaultShortcuts(QAction *action) const {
|
||||
return defaultShortcuts.values(cfgKey(action));
|
||||
}
|
||||
|
||||
QList<QKeySequence> ShortcutsConfig::getDefaultShortcuts(Shortcut *shortcut) const {
|
||||
return defaultShortcuts.values(cfgKey(shortcut));
|
||||
}
|
||||
|
||||
void ShortcutsConfig::setUserShortcuts(const QList<QAction *> &actions) {
|
||||
storeShortcuts(StoreType::User, actions);
|
||||
void ShortcutsConfig::setUserShortcuts(const QMultiMap<const QObject *, QKeySequence> &objects_keySequences) {
|
||||
for (auto *object : objects_keySequences.uniqueKeys())
|
||||
storeShortcuts(StoreType::User, cfgKey(object), objects_keySequences.values(object));
|
||||
save();
|
||||
}
|
||||
|
||||
void ShortcutsConfig::setUserShortcuts(const QList<Shortcut *> &shortcuts) {
|
||||
storeShortcuts(StoreType::User, shortcuts);
|
||||
save();
|
||||
QList<QKeySequence> ShortcutsConfig::userShortcuts(const QObject *object) const {
|
||||
return user_shortcuts.values(cfgKey(object));
|
||||
}
|
||||
|
||||
void ShortcutsConfig::setUserShortcuts(const QList<QAction *> &actions, const QList<Shortcut *> &shortcuts) {
|
||||
storeShortcuts(StoreType::User, actions);
|
||||
storeShortcuts(StoreType::User, shortcuts);
|
||||
save();
|
||||
void ShortcutsConfig::storeShortcutsFromList(StoreType storeType, const QObjectList &objects) {
|
||||
for (const auto *object : objects)
|
||||
if (objectNameIsValid(object))
|
||||
storeShortcuts(storeType, cfgKey(object), currentShortcuts(object));
|
||||
}
|
||||
|
||||
QList<QKeySequence> ShortcutsConfig::getUserShortcuts(QAction *action) const {
|
||||
return userShortcuts.values(cfgKey(action));
|
||||
}
|
||||
|
||||
QList<QKeySequence> ShortcutsConfig::getUserShortcuts(Shortcut *shortcut) const {
|
||||
return userShortcuts.values(cfgKey(shortcut));
|
||||
}
|
||||
|
||||
void ShortcutsConfig::storeShortcuts(StoreType storeType, const QList<QAction *> &actions) {
|
||||
for (auto *action : actions)
|
||||
if (!action->text().isEmpty() && !action->objectName().isEmpty())
|
||||
storeShortcut(storeType, cfgKey(action), action->shortcuts());
|
||||
}
|
||||
|
||||
void ShortcutsConfig::storeShortcuts(StoreType storeType, const QList<Shortcut *> &shortcuts) {
|
||||
for (auto *shortcut : shortcuts)
|
||||
if (!shortcut->whatsThis().isEmpty() && !shortcut->objectName().isEmpty())
|
||||
storeShortcut(storeType, cfgKey(shortcut), shortcut->keys());
|
||||
}
|
||||
|
||||
void ShortcutsConfig::storeShortcut(
|
||||
void ShortcutsConfig::storeShortcuts(
|
||||
StoreType storeType,
|
||||
const QString &cfgKey,
|
||||
const QList<QKeySequence> &keySequences)
|
||||
{
|
||||
bool storeUser = (storeType == User) || !userShortcuts.contains(cfgKey);
|
||||
bool storeUser = (storeType == User) || !user_shortcuts.contains(cfgKey);
|
||||
|
||||
if (storeType == Default)
|
||||
defaultShortcuts.remove(cfgKey);
|
||||
default_shortcuts.remove(cfgKey);
|
||||
if (storeUser)
|
||||
userShortcuts.remove(cfgKey);
|
||||
user_shortcuts.remove(cfgKey);
|
||||
|
||||
if (keySequences.isEmpty()) {
|
||||
if (storeType == Default)
|
||||
defaultShortcuts.insert(cfgKey, QKeySequence());
|
||||
default_shortcuts.insert(cfgKey, QKeySequence());
|
||||
if (storeUser)
|
||||
userShortcuts.insert(cfgKey, QKeySequence());
|
||||
user_shortcuts.insert(cfgKey, QKeySequence());
|
||||
} else {
|
||||
for (auto keySequence : keySequences) {
|
||||
if (storeType == Default)
|
||||
defaultShortcuts.insert(cfgKey, keySequence);
|
||||
default_shortcuts.insert(cfgKey, keySequence);
|
||||
if (storeUser)
|
||||
userShortcuts.insert(cfgKey, keySequence);
|
||||
user_shortcuts.insert(cfgKey, keySequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ShortcutsConfig::objectNameIsValid(const QObject *object) {
|
||||
// Qt internal action names start with "_q_" so we filter those out.
|
||||
return !object->objectName().isEmpty() && !object->objectName().startsWith("_q_");
|
||||
}
|
||||
|
||||
/* Creates a config key from the object's name prepended with the parent
|
||||
* window's object name, and converts camelCase to snake_case. */
|
||||
QString ShortcutsConfig::cfgKey(QObject *object) const {
|
||||
QString ShortcutsConfig::cfgKey(const QObject *object) const {
|
||||
auto cfg_key = QString();
|
||||
auto *parentWidget = static_cast<QWidget *>(object->parent());
|
||||
if (parentWidget)
|
||||
|
@ -838,11 +817,31 @@ QString ShortcutsConfig::cfgKey(QObject *object) const {
|
|||
cfg_key += object->objectName();
|
||||
|
||||
QRegularExpression re("[A-Z]");
|
||||
int i = cfg_key.indexOf(re);
|
||||
int i = cfg_key.indexOf(re, 1);
|
||||
while (i != -1) {
|
||||
if (i != 0 && cfg_key.at(i - 1) != '_')
|
||||
if (cfg_key.at(i - 1) != '_')
|
||||
cfg_key.insert(i++, '_');
|
||||
i = cfg_key.indexOf(re, i + 1);
|
||||
}
|
||||
return cfg_key.toLower();
|
||||
}
|
||||
|
||||
QList<QKeySequence> ShortcutsConfig::currentShortcuts(const QObject *object) const {
|
||||
if (object->inherits("QAction")) {
|
||||
const auto *action = qobject_cast<const QAction *>(object);
|
||||
return action->shortcuts();
|
||||
} else if (object->inherits("QAbstractButton")) {
|
||||
const auto *button = qobject_cast<const QAbstractButton *>(object);
|
||||
return { button->shortcut() };
|
||||
} else if (object->inherits("Shortcut")) {
|
||||
const auto *shortcut = qobject_cast<const Shortcut *>(object);
|
||||
return shortcut->keys();
|
||||
} else if (object->inherits("QShortcut")) {
|
||||
const auto *qshortcut = qobject_cast<const QShortcut *>(object);
|
||||
return { qshortcut->key() };
|
||||
} else if (object->property("shortcut").isValid()) {
|
||||
return { object->property("shortcut").value<QKeySequence>() };
|
||||
} else {
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,40 +88,63 @@ void MainWindow::initWindow() {
|
|||
porymapConfig.load();
|
||||
this->initCustomUI();
|
||||
this->initExtraSignals();
|
||||
this->initExtraShortcuts();
|
||||
this->initEditor();
|
||||
this->initMiscHeapObjects();
|
||||
this->initMapSortOrder();
|
||||
this->initUserShortcuts();
|
||||
this->initShortcuts();
|
||||
this->restoreWindowState();
|
||||
|
||||
setWindowDisabled(true);
|
||||
}
|
||||
|
||||
void MainWindow::initShortcuts() {
|
||||
initExtraShortcuts();
|
||||
|
||||
shortcutsConfig.load();
|
||||
shortcutsConfig.setDefaultShortcuts(shortcutableObjects());
|
||||
applyUserShortcuts();
|
||||
}
|
||||
|
||||
void MainWindow::initExtraShortcuts() {
|
||||
auto *shortcutReset_Zoom = new Shortcut(QKeySequence("Ctrl+0"), this, SLOT(resetMapViewScale()));
|
||||
shortcutReset_Zoom->setObjectName("shortcutReset_Zoom");
|
||||
shortcutReset_Zoom->setObjectName("shortcutZoom_Reset");
|
||||
shortcutReset_Zoom->setWhatsThis("Zoom Reset");
|
||||
|
||||
auto *shortcutToggle_Grid = new Shortcut(QKeySequence("Ctrl+G"), ui->checkBox_ToggleGrid, SLOT(toggle()));
|
||||
shortcutToggle_Grid->setObjectName("shortcutToggle_Grid");
|
||||
shortcutToggle_Grid->setWhatsThis("Toggle Grid");
|
||||
|
||||
auto *shortcutDuplicate_Events = new Shortcut(QKeySequence("Ctrl+D"), this, SLOT(duplicate()));
|
||||
shortcutDuplicate_Events->setObjectName("shortcutDuplicate_Events");
|
||||
shortcutDuplicate_Events->setWhatsThis("Duplicate Selected Event(s)");
|
||||
|
||||
auto *shortcutDelete_Object = new Shortcut(
|
||||
{QKeySequence("Del"), QKeySequence("Backspace")}, this, SLOT(on_toolButton_deleteObject_clicked()));
|
||||
shortcutDelete_Object->setObjectName("shortcutDelete_Object");
|
||||
shortcutDelete_Object->setWhatsThis("Delete Selected Event(s)");
|
||||
|
||||
ui->actionZoom_In->setShortcuts({ui->actionZoom_In->shortcut(), QKeySequence("Ctrl+=")});
|
||||
}
|
||||
|
||||
void MainWindow::initUserShortcuts() {
|
||||
shortcutsConfig.load();
|
||||
shortcutsConfig.setDefaultShortcuts(findChildren<QAction *>(), findChildren<Shortcut *>());
|
||||
QObjectList MainWindow::shortcutableObjects() const {
|
||||
QObjectList shortcutable_objects;
|
||||
for (auto *action : findChildren<QAction *>())
|
||||
action->setShortcuts(shortcutsConfig.getUserShortcuts(action));
|
||||
if (!action->objectName().isEmpty())
|
||||
shortcutable_objects.append(qobject_cast<QObject *>(action));
|
||||
for (auto *shortcut : findChildren<Shortcut *>())
|
||||
shortcut->setKeys(shortcutsConfig.getUserShortcuts(shortcut));
|
||||
if (!shortcut->objectName().isEmpty())
|
||||
shortcutable_objects.append(qobject_cast<QObject *>(shortcut));
|
||||
|
||||
return shortcutable_objects;
|
||||
}
|
||||
|
||||
void MainWindow::applyUserShortcuts() {
|
||||
for (auto *action : findChildren<QAction *>())
|
||||
if (!action->objectName().isEmpty())
|
||||
action->setShortcuts(shortcutsConfig.userShortcuts(action));
|
||||
for (auto *shortcut : findChildren<Shortcut *>())
|
||||
if (!shortcut->objectName().isEmpty())
|
||||
shortcut->setKeys(shortcutsConfig.userShortcuts(shortcut));
|
||||
}
|
||||
|
||||
void MainWindow::initCustomUI() {
|
||||
|
@ -1426,8 +1449,12 @@ void MainWindow::on_actionUse_Poryscript_triggered(bool checked)
|
|||
|
||||
void MainWindow::on_actionEdit_Shortcuts_triggered()
|
||||
{
|
||||
if (!shortcutsEditor)
|
||||
shortcutsEditor = new ShortcutsEditor(this);
|
||||
if (!shortcutsEditor) {
|
||||
shortcutsEditor = new ShortcutsEditor(shortcutableObjects(), this);
|
||||
connect(shortcutsEditor, &QObject::destroyed, [=](QObject *) { shortcutsEditor = nullptr; });
|
||||
connect(shortcutsEditor, &ShortcutsEditor::shortcutsSaved,
|
||||
this, &MainWindow::applyUserShortcuts);
|
||||
}
|
||||
|
||||
if (shortcutsEditor->isHidden())
|
||||
shortcutsEditor->show();
|
||||
|
|
|
@ -1,26 +1,32 @@
|
|||
#include "shortcutseditor.h"
|
||||
#include "ui_shortcutseditor.h"
|
||||
#include "config.h"
|
||||
#include "multikeyedit.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QFormLayout>
|
||||
#include <QAbstractButton>
|
||||
#include <QtEvents>
|
||||
#include <QMessageBox>
|
||||
#include <QKeySequenceEdit>
|
||||
#include <QAction>
|
||||
#include <QRegularExpression>
|
||||
#include <QLabel>
|
||||
|
||||
|
||||
ShortcutsEditor::ShortcutsEditor(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ShortcutsEditor::ShortcutsEditor(const QObjectList &objectList, QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::ShortcutsEditor),
|
||||
actions(QMap<QString, QAction *>())
|
||||
main_container(nullptr)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ase_container = ui->scrollAreaWidgetContents_Shortcuts;
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
main_container = ui->scrollAreaWidgetContents_Shortcuts;
|
||||
main_container->setLayout(new QVBoxLayout(main_container));
|
||||
connect(ui->buttonBox, &QDialogButtonBox::clicked,
|
||||
this, &ShortcutsEditor::dialogButtonClicked);
|
||||
populateShortcuts();
|
||||
|
||||
parseObjectList(objectList);
|
||||
populateMainContainer();
|
||||
}
|
||||
|
||||
ShortcutsEditor::~ShortcutsEditor()
|
||||
|
@ -28,207 +34,143 @@ ShortcutsEditor::~ShortcutsEditor()
|
|||
delete ui;
|
||||
}
|
||||
|
||||
void ShortcutsEditor::populateShortcuts() {
|
||||
if (!parent())
|
||||
return;
|
||||
void ShortcutsEditor::saveShortcuts() {
|
||||
QMultiMap<const QObject *, QKeySequence> objects_keySequences;
|
||||
for (auto it = multiKeyEdits_objects.cbegin(); it != multiKeyEdits_objects.cend(); ++it)
|
||||
for (auto keySequence : it.key()->keySequences())
|
||||
objects_keySequences.insert(it.value(), keySequence);
|
||||
|
||||
for (auto action : parent()->findChildren<QAction *>())
|
||||
if (!action->text().isEmpty() && !action->objectName().isEmpty())
|
||||
actions.insert(action->text().remove('&'), action);
|
||||
shortcutsConfig.setUserShortcuts(objects_keySequences);
|
||||
emit shortcutsSaved();
|
||||
}
|
||||
|
||||
auto *formLayout = new QFormLayout(ase_container);
|
||||
for (auto *action : actions) {
|
||||
auto userShortcuts = shortcutsConfig.getUserShortcuts(action);
|
||||
auto *ase = new ActionShortcutEdit(ase_container, action, 2);
|
||||
connect(ase, &ActionShortcutEdit::editingFinished,
|
||||
this, &ShortcutsEditor::checkForDuplicates);
|
||||
ase->setShortcuts(userShortcuts);
|
||||
formLayout->addRow(action->text(), ase);
|
||||
// Restores default shortcuts but doesn't save until Apply or OK is clicked.
|
||||
void ShortcutsEditor::resetShortcuts() {
|
||||
for (auto it = multiKeyEdits_objects.begin(); it != multiKeyEdits_objects.end(); ++it) {
|
||||
it.key()->blockSignals(true);
|
||||
const auto defaults = shortcutsConfig.defaultShortcuts(it.value());
|
||||
it.key()->setKeySequences(defaults);
|
||||
it.key()->blockSignals(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutsEditor::saveShortcuts() {
|
||||
auto ase_children = ase_container->findChildren<ActionShortcutEdit *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
for (auto *ase : ase_children)
|
||||
ase->applyShortcuts();
|
||||
shortcutsConfig.setUserShortcuts(actions.values());
|
||||
void ShortcutsEditor::parseObjectList(const QObjectList &objectList) {
|
||||
for (auto *object : objectList) {
|
||||
const auto label = getLabel(object);
|
||||
if (!label.isEmpty() && ShortcutsConfig::objectNameIsValid(object))
|
||||
labels_objects.insert(label, object);
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutsEditor::resetShortcuts() {
|
||||
auto ase_children = ase_container->findChildren<ActionShortcutEdit *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
for (auto *ase : ase_children)
|
||||
ase->setShortcuts(shortcutsConfig.getDefaultShortcuts(ase->action));
|
||||
QString ShortcutsEditor::getLabel(const QObject *object) const {
|
||||
if (stringPropertyIsNotEmpty(object, "text"))
|
||||
return object->property("text").toString().remove('&');
|
||||
else if (stringPropertyIsNotEmpty(object, "whatsThis"))
|
||||
return object->property("whatsThis").toString().remove('&');
|
||||
else
|
||||
return QString();
|
||||
}
|
||||
|
||||
void ShortcutsEditor::promptUser(ActionShortcutEdit *current, ActionShortcutEdit *sender) {
|
||||
auto result = QMessageBox::question(
|
||||
this,
|
||||
"porymap",
|
||||
QString("Shortcut \"%1\" is already used by \"%2\", would you like to replace it?")
|
||||
.arg(sender->last().toString()).arg(current->action->text()),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
bool ShortcutsEditor::stringPropertyIsNotEmpty(const QObject *object, const char *name) const {
|
||||
return object->property(name).isValid() && !object->property(name).toString().isEmpty();
|
||||
}
|
||||
|
||||
void ShortcutsEditor::populateMainContainer() {
|
||||
for (auto object : labels_objects) {
|
||||
const auto shortcutContext = getShortcutContext(object);
|
||||
if (!contexts_layouts.contains(shortcutContext))
|
||||
addNewContextGroup(shortcutContext);
|
||||
|
||||
addNewMultiKeyEdit(object, shortcutContext);
|
||||
}
|
||||
}
|
||||
|
||||
// The context for which the object's shortcut is active (Displayed in group box title).
|
||||
// Uses the parent window's objectName and adds spaces between words.
|
||||
QString ShortcutsEditor::getShortcutContext(const QObject *object) const {
|
||||
auto objectParentWidget = static_cast<QWidget *>(object->parent());
|
||||
auto context = objectParentWidget->window()->objectName();
|
||||
QRegularExpression re("[A-Z]");
|
||||
int i = context.indexOf(re, 1);
|
||||
while (i != -1) {
|
||||
if (context.at(i - 1) != ' ')
|
||||
context.insert(i++, ' ');
|
||||
i = context.indexOf(re, i + 1);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
// Seperate shortcuts into context groups for duplicate checking.
|
||||
void ShortcutsEditor::addNewContextGroup(const QString &shortcutContext) {
|
||||
auto *groupBox = new QGroupBox(shortcutContext, main_container);
|
||||
main_container->layout()->addWidget(groupBox);
|
||||
auto *formLayout = new QFormLayout(groupBox);
|
||||
contexts_layouts.insert(shortcutContext, formLayout);
|
||||
}
|
||||
|
||||
void ShortcutsEditor::addNewMultiKeyEdit(const QObject *object, const QString &shortcutContext) {
|
||||
auto *container = contexts_layouts.value(shortcutContext)->parentWidget();
|
||||
auto *multiKeyEdit = new MultiKeyEdit(container);
|
||||
multiKeyEdit->setKeySequences(shortcutsConfig.userShortcuts(object));
|
||||
connect(multiKeyEdit, &MultiKeyEdit::keySequenceChanged,
|
||||
this, &ShortcutsEditor::checkForDuplicates);
|
||||
contexts_layouts.value(shortcutContext)->addRow(labels_objects.key(object), multiKeyEdit);
|
||||
multiKeyEdits_objects.insert(multiKeyEdit, object);
|
||||
}
|
||||
|
||||
void ShortcutsEditor::checkForDuplicates(const QKeySequence &keySequence) {
|
||||
if (keySequence.isEmpty())
|
||||
return;
|
||||
|
||||
auto *sender_multiKeyEdit = qobject_cast<MultiKeyEdit *>(sender());
|
||||
if (!sender_multiKeyEdit)
|
||||
return;
|
||||
|
||||
for (auto *sibling_multiKeyEdit : siblings(sender_multiKeyEdit))
|
||||
if (sibling_multiKeyEdit->contains(keySequence))
|
||||
promptUserOnDuplicateFound(sender_multiKeyEdit, sibling_multiKeyEdit);
|
||||
}
|
||||
|
||||
QList<MultiKeyEdit *> ShortcutsEditor::siblings(MultiKeyEdit *multiKeyEdit) const {
|
||||
auto list = multiKeyEdit->parent()->findChildren<MultiKeyEdit *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
list.removeOne(multiKeyEdit);
|
||||
return list;
|
||||
}
|
||||
|
||||
void ShortcutsEditor::promptUserOnDuplicateFound(MultiKeyEdit *sender, MultiKeyEdit *sibling) {
|
||||
const auto duplicateKeySequence = sender->keySequences().last();
|
||||
const auto siblingLabel = getLabel(multiKeyEdits_objects.value(sibling));
|
||||
const auto message = QString(
|
||||
"Shortcut '%1' is already used by '%2', would you like to replace it?")
|
||||
.arg(duplicateKeySequence.toString()).arg(siblingLabel);
|
||||
|
||||
const auto result = QMessageBox::question(
|
||||
this, "porymap", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
|
||||
if (result == QMessageBox::Yes)
|
||||
current->removeOne(sender->last());
|
||||
else if (result == QMessageBox::No)
|
||||
sender->removeOne(sender->last());
|
||||
removeKeySequence(duplicateKeySequence, sibling);
|
||||
else
|
||||
removeKeySequence(duplicateKeySequence, sender);
|
||||
|
||||
activateWindow();
|
||||
}
|
||||
|
||||
void ShortcutsEditor::checkForDuplicates() {
|
||||
auto *sender_ase = qobject_cast<ActionShortcutEdit *>(sender());
|
||||
if (!sender_ase)
|
||||
return;
|
||||
|
||||
for (auto *child_kse : findChildren<QKeySequenceEdit *>()) {
|
||||
if (child_kse->keySequence().isEmpty() || child_kse->parent() == sender())
|
||||
continue;
|
||||
|
||||
if (sender_ase->contains(child_kse->keySequence())) {
|
||||
auto *current_ase = qobject_cast<ActionShortcutEdit *>(child_kse->parent());
|
||||
if (!current_ase)
|
||||
continue;
|
||||
|
||||
promptUser(current_ase, sender_ase);
|
||||
activateWindow();
|
||||
return;
|
||||
}
|
||||
}
|
||||
void ShortcutsEditor::removeKeySequence(const QKeySequence &keySequence, MultiKeyEdit *multiKeyEdit) {
|
||||
multiKeyEdit->blockSignals(true);
|
||||
multiKeyEdit->removeOne(keySequence);
|
||||
multiKeyEdit->blockSignals(false);
|
||||
}
|
||||
|
||||
void ShortcutsEditor::dialogButtonClicked(QAbstractButton *button) {
|
||||
auto buttonRole = ui->buttonBox->buttonRole(button);
|
||||
if (buttonRole == QDialogButtonBox::AcceptRole) {
|
||||
saveShortcuts();
|
||||
hide();
|
||||
close();
|
||||
} else if (buttonRole == QDialogButtonBox::ApplyRole) {
|
||||
saveShortcuts();
|
||||
} else if (buttonRole == QDialogButtonBox::RejectRole) {
|
||||
hide();
|
||||
close();
|
||||
} else if (buttonRole == QDialogButtonBox::ResetRole) {
|
||||
resetShortcuts();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ActionShortcutEdit::ActionShortcutEdit(QWidget *parent, QAction *action, int count) :
|
||||
QWidget(parent),
|
||||
action(action),
|
||||
kse_children(QVector<QKeySequenceEdit *>()),
|
||||
ks_list(QList<QKeySequence>())
|
||||
{
|
||||
setLayout(new QHBoxLayout(this));
|
||||
layout()->setContentsMargins(0, 0, 0, 0);
|
||||
setCount(count);
|
||||
}
|
||||
|
||||
bool ActionShortcutEdit::eventFilter(QObject *watched, QEvent *event) {
|
||||
auto *watched_kse = qobject_cast<QKeySequenceEdit *>(watched);
|
||||
if (!watched_kse)
|
||||
return false;
|
||||
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
auto *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
watched_kse->clearFocus();
|
||||
return true;
|
||||
} else if (keyEvent->key() == Qt::Key_Backspace) {
|
||||
removeOne(watched_kse->keySequence());
|
||||
return true;
|
||||
} else {
|
||||
watched_kse->clear();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ActionShortcutEdit::setCount(int count) {
|
||||
if (count < 1)
|
||||
count = 1;
|
||||
|
||||
while (kse_children.count() > count) {
|
||||
layout()->removeWidget(kse_children.last());
|
||||
delete kse_children.last();
|
||||
kse_children.removeLast();
|
||||
}
|
||||
|
||||
while (kse_children.count() < count) {
|
||||
auto *kse = new QKeySequenceEdit(this);
|
||||
connect(kse, &QKeySequenceEdit::editingFinished,
|
||||
this, &ActionShortcutEdit::onEditingFinished);
|
||||
kse->installEventFilter(this);
|
||||
layout()->addWidget(kse);
|
||||
kse_children.append(kse);
|
||||
}
|
||||
}
|
||||
|
||||
QList<QKeySequence> ActionShortcutEdit::shortcuts() const {
|
||||
QList<QKeySequence> current_ks_list;
|
||||
for (auto *kse : kse_children)
|
||||
if (!kse->keySequence().isEmpty())
|
||||
current_ks_list.append(kse->keySequence());
|
||||
return current_ks_list;
|
||||
}
|
||||
|
||||
void ActionShortcutEdit::setShortcuts(const QList<QKeySequence> &keySequences) {
|
||||
clear();
|
||||
ks_list = keySequences;
|
||||
int minCount = qMin(kse_children.count(), ks_list.count());
|
||||
for (int i = 0; i < minCount; ++i)
|
||||
kse_children[i]->setKeySequence(ks_list[i]);
|
||||
}
|
||||
|
||||
void ActionShortcutEdit::applyShortcuts() {
|
||||
action->setShortcuts(shortcuts());
|
||||
}
|
||||
|
||||
bool ActionShortcutEdit::removeOne(const QKeySequence &keySequence) {
|
||||
for (auto *kse : kse_children) {
|
||||
if (kse->keySequence() == keySequence) {
|
||||
ks_list.removeOne(keySequence);
|
||||
kse->clear();
|
||||
updateShortcuts();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActionShortcutEdit::contains(const QKeySequence &keySequence) {
|
||||
for (auto ks : shortcuts())
|
||||
if (ks == keySequence)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActionShortcutEdit::contains(QKeySequenceEdit *keySequenceEdit) {
|
||||
for (auto *kse : kse_children)
|
||||
if (kse == keySequenceEdit)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ActionShortcutEdit::clear() {
|
||||
for (auto *kse : kse_children)
|
||||
kse->clear();
|
||||
}
|
||||
|
||||
void ActionShortcutEdit::focusLast() {
|
||||
for (int i = count() - 1; i >= 0; --i) {
|
||||
if (!kse_children[i]->keySequence().isEmpty()) {
|
||||
kse_children[i]->setFocus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionShortcutEdit::onEditingFinished() {
|
||||
auto *kse = qobject_cast<QKeySequenceEdit *>(sender());
|
||||
if (!kse)
|
||||
return;
|
||||
|
||||
if (ks_list.contains(kse->keySequence()))
|
||||
removeOne(kse->keySequence());
|
||||
updateShortcuts();
|
||||
focusLast();
|
||||
emit editingFinished();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue