Show conflicting shortcut in error message (#5287)

This commit is contained in:
RickyRister 2024-12-21 17:58:55 -08:00 committed by GitHub
parent 23099f7e8b
commit 4823cce622
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 91 additions and 44 deletions

View file

@ -114,6 +114,17 @@ QString ShortcutsSettings::getShortcutString(const QString &name) const
return stringifySequence(getShortcut(name)); return stringifySequence(getShortcut(name));
} }
QString ShortcutsSettings::getShortcutFriendlyName(const QString &shortcutName) const
{
for (auto it = defaultShortCuts.cbegin(); it != defaultShortCuts.cend(); ++it) {
if (shortcutName == it.key()) {
return it.value().getName();
}
}
return {};
}
QString ShortcutsSettings::stringifySequence(const QList<QKeySequence> &Sequence) const QString ShortcutsSettings::stringifySequence(const QList<QKeySequence> &Sequence) const
{ {
QStringList stringSequence; QStringList stringSequence;
@ -150,9 +161,9 @@ void ShortcutsSettings::setShortcuts(const QString &name, const QKeySequence &Se
setShortcuts(name, QList<QKeySequence>{Sequence}); setShortcuts(name, QList<QKeySequence>{Sequence});
} }
void ShortcutsSettings::setShortcuts(const QString &name, const QString &Sequences) void ShortcutsSettings::setShortcuts(const QString &name, const QString &sequences)
{ {
setShortcuts(name, parseSequenceString(Sequences)); setShortcuts(name, parseSequenceString(sequences));
} }
void ShortcutsSettings::resetAllShortcuts() void ShortcutsSettings::resetAllShortcuts()
@ -177,33 +188,45 @@ void ShortcutsSettings::clearAllShortcuts()
emit shortCutChanged(); emit shortCutChanged();
} }
bool ShortcutsSettings::isKeyAllowed(const QString &name, const QString &Sequences) const bool ShortcutsSettings::isKeyAllowed(const QString &name, const QString &sequences) const
{ {
// if the shortcut is not to be used in deck-editor then it doesn't matter // if the shortcut is not to be used in deck-editor then it doesn't matter
if (name.startsWith("Player") || name.startsWith("Replays")) { if (name.startsWith("Player") || name.startsWith("Replays")) {
return true; return true;
} }
QString checkSequence = Sequences.split(sep).last(); QString checkSequence = sequences.split(sep).last();
QStringList forbiddenKeys{"Del", "Backspace", "Down", "Up", "Left", "Right", QStringList forbiddenKeys{"Del", "Backspace", "Down", "Up", "Left", "Right",
"Return", "Enter", "Menu", "Ctrl+Alt+-", "Ctrl+Alt+=", "Ctrl+Alt+[", "Return", "Enter", "Menu", "Ctrl+Alt+-", "Ctrl+Alt+=", "Ctrl+Alt+[",
"Ctrl+Alt+]", "Tab", "Space", "Shift+S", "Shift+Left", "Shift+Right"}; "Ctrl+Alt+]", "Tab", "Space", "Shift+S", "Shift+Left", "Shift+Right"};
return !forbiddenKeys.contains(checkSequence); return !forbiddenKeys.contains(checkSequence);
} }
bool ShortcutsSettings::isValid(const QString &name, const QString &Sequences) const /**
* Checks that the shortcut doesn't overlap with an existing shortcut
*
* @param name The name of the shortcut
* @param sequences The shortcut key sequence
* @return Whether the shortcut is valid.
*/
bool ShortcutsSettings::isValid(const QString &name, const QString &sequences) const
{ {
QString checkSequence = Sequences.split(sep).last(); return findOverlaps(name, sequences).isEmpty();
}
QStringList ShortcutsSettings::findOverlaps(const QString &name, const QString &sequences) const
{
QString checkSequence = sequences.split(sep).last();
QString checkKey = name.left(name.indexOf("/")); QString checkKey = name.left(name.indexOf("/"));
QList<QString> allKeys = shortCuts.keys(); QStringList overlaps;
for (const auto &key : allKeys) { for (const auto &key : shortCuts.keys()) {
if (key.startsWith(checkKey) || key.startsWith("MainWindow") || checkKey.startsWith("MainWindow")) { if (key.startsWith(checkKey) || key.startsWith("MainWindow") || checkKey.startsWith("MainWindow")) {
QString storedSequence = stringifySequence(shortCuts.value(key)); QString storedSequence = stringifySequence(shortCuts.value(key));
QStringList stringSequences = storedSequence.split(sep); if (storedSequence.split(sep).contains(checkSequence)) {
if (stringSequences.contains(checkSequence)) { overlaps.append(getShortcutFriendlyName(key));
return false;
} }
} }
} }
return true;
} return overlaps;
}

View file

@ -2,9 +2,7 @@
#define SHORTCUTSSETTINGS_H #define SHORTCUTSSETTINGS_H
#include <QApplication> #include <QApplication>
#include <QHash>
#include <QKeySequence> #include <QKeySequence>
#include <QObject>
#include <QSettings> #include <QSettings>
class ShortcutGroup class ShortcutGroup
@ -80,18 +78,18 @@ public:
class ShortcutKey : public QList<QKeySequence> class ShortcutKey : public QList<QKeySequence>
{ {
public: public:
ShortcutKey(const QString &_name = QString(), explicit ShortcutKey(const QString &_name = QString(),
QList<QKeySequence> _sequence = QList<QKeySequence>(), QList _sequence = QList(),
ShortcutGroup::Groups _group = ShortcutGroup::Main_Window); ShortcutGroup::Groups _group = ShortcutGroup::Main_Window);
void setSequence(QList<QKeySequence> _sequence) void setSequence(const QList &_sequence)
{ {
QList<QKeySequence>::operator=(_sequence); QList::operator=(_sequence);
}; };
const QString getName() const QString getName() const
{ {
return QApplication::translate("shortcutsTab", name.toUtf8().data()); return QApplication::translate("shortcutsTab", name.toUtf8().data());
}; };
const QString getGroupName() const QString getGroupName() const
{ {
return ShortcutGroup::getGroupName(group); return ShortcutGroup::getGroupName(group);
}; };
@ -105,13 +103,14 @@ class ShortcutsSettings : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
ShortcutsSettings(const QString &settingsFilePath, QObject *parent = nullptr); explicit ShortcutsSettings(const QString &settingsFilePath, QObject *parent = nullptr);
ShortcutKey getDefaultShortcut(const QString &name) const; ShortcutKey getDefaultShortcut(const QString &name) const;
ShortcutKey getShortcut(const QString &name) const; ShortcutKey getShortcut(const QString &name) const;
QKeySequence getSingleShortcut(const QString &name) const; QKeySequence getSingleShortcut(const QString &name) const;
QString getDefaultShortcutString(const QString &name) const; QString getDefaultShortcutString(const QString &name) const;
QString getShortcutString(const QString &name) const; QString getShortcutString(const QString &name) const;
QString getShortcutFriendlyName(const QString &shortcutName) const;
QList<QString> getAllShortcutKeys() const QList<QString> getAllShortcutKeys() const
{ {
return shortCuts.keys(); return shortCuts.keys();
@ -119,10 +118,11 @@ public:
void setShortcuts(const QString &name, const QList<QKeySequence> &Sequence); void setShortcuts(const QString &name, const QList<QKeySequence> &Sequence);
void setShortcuts(const QString &name, const QKeySequence &Sequence); void setShortcuts(const QString &name, const QKeySequence &Sequence);
void setShortcuts(const QString &name, const QString &Sequences); void setShortcuts(const QString &name, const QString &sequences);
bool isKeyAllowed(const QString &name, const QString &Sequences) const; bool isKeyAllowed(const QString &name, const QString &sequences) const;
bool isValid(const QString &name, const QString &Sequences) const; bool isValid(const QString &name, const QString &sequences) const;
QStringList findOverlaps(const QString &name, const QString &sequences) const;
void resetAllShortcuts(); void resetAllShortcuts();
void clearAllShortcuts(); void clearAllShortcuts();

View file

@ -154,26 +154,49 @@ int SequenceEdit::translateModifiers(Qt::KeyboardModifiers state, const QString
return result; return result;
} }
/**
*Validates that shortcut is valid (is a valid shortcut key sequence and doesn't conflict with any other shortcuts).
*Displays warning messages if it's not valid.
*
* @param sequence The shortcut key sequence
* @return True if the sequence isn't already self-contained
*/
bool SequenceEdit::validateShortcut(const QKeySequence &sequence)
{
if (sequence.isEmpty() || !valid) {
return true;
}
const auto &shortcutsSettings = SettingsCache::instance().shortcuts();
const QString sequenceString = sequence.toString();
if (!shortcutsSettings.isKeyAllowed(shortcutName, sequenceString)) {
QToolTip::showText(lineEdit->mapToGlobal(QPoint()), tr("Invalid key"));
return true;
}
if (!shortcutsSettings.isValid(shortcutName, sequenceString)) {
auto overlaps = shortcutsSettings.findOverlaps(shortcutName, sequenceString);
QToolTip::showText(lineEdit->mapToGlobal(QPoint()),
tr("Shortcut already in use by:") + " " + overlaps.join(", "));
return true;
}
if (!lineEdit->text().isEmpty()) {
if (lineEdit->text().contains(sequenceString)) {
return false;
}
lineEdit->setText(lineEdit->text() + ";");
}
lineEdit->setText(lineEdit->text() + sequenceString);
return true;
}
void SequenceEdit::finishShortcut() void SequenceEdit::finishShortcut()
{ {
QKeySequence sequence(keys); if (!validateShortcut(QKeySequence(keys))) {
if (!sequence.isEmpty() && valid) { return;
QString sequenceString = sequence.toString();
if (SettingsCache::instance().shortcuts().isKeyAllowed(shortcutName, sequenceString)) {
if (SettingsCache::instance().shortcuts().isValid(shortcutName, sequenceString)) {
if (!lineEdit->text().isEmpty()) {
if (lineEdit->text().contains(sequenceString)) {
return;
}
lineEdit->setText(lineEdit->text() + ";");
}
lineEdit->setText(lineEdit->text() + sequenceString);
} else {
QToolTip::showText(lineEdit->mapToGlobal(QPoint()), tr("Shortcut already in use"));
}
} else {
QToolTip::showText(lineEdit->mapToGlobal(QPoint()), tr("Invalid key"));
}
} }
currentKey = 0; currentKey = 0;

View file

@ -36,6 +36,7 @@ private:
void processKey(QKeyEvent *e); void processKey(QKeyEvent *e);
int translateModifiers(Qt::KeyboardModifiers state, const QString &text); int translateModifiers(Qt::KeyboardModifiers state, const QString &text);
bool validateShortcut(const QKeySequence &sequence);
void finishShortcut(); void finishShortcut();
void updateSettings(); void updateSettings();
}; };