Base key config/shortcut implementation
parent
4ac5174d89
commit
40297701d0
@ -0,0 +1,139 @@
|
||||
#include "shortcut.hpp"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QShortcut>
|
||||
|
||||
#include "state.hpp"
|
||||
#include "shortcutmanager.hpp"
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
Shortcut::Shortcut(const std::string& name, QObject* parent)
|
||||
: QObject(parent)
|
||||
, mName(name)
|
||||
, mCurrentPos(0)
|
||||
, mLastPos(0)
|
||||
{
|
||||
State::get().getShortcutManager().addShortcut(this);
|
||||
setSequence(State::get().getShortcutManager().getSequence(name));
|
||||
}
|
||||
|
||||
Shortcut::~Shortcut()
|
||||
{
|
||||
State::get().getShortcutManager().removeShortcut(this);
|
||||
}
|
||||
|
||||
const std::string& Shortcut::getName() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
const QKeySequence& Shortcut::getSequence() const
|
||||
{
|
||||
return mSequence;
|
||||
}
|
||||
|
||||
void Shortcut::setSequence(const QKeySequence& sequence)
|
||||
{
|
||||
mSequence = sequence;
|
||||
mCurrentPos = 0;
|
||||
mLastPos = sequence.count() - 1;
|
||||
}
|
||||
|
||||
void Shortcut::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
int withMod = event->key() | event->modifiers();
|
||||
int noMod = event->key();
|
||||
|
||||
if (withMod == mSequence[mCurrentPos] || (mCurrentPos > 0 && noMod == mSequence[mCurrentPos]))
|
||||
{
|
||||
if (mCurrentPos == mLastPos)
|
||||
{
|
||||
activated(true);
|
||||
}
|
||||
else
|
||||
++mCurrentPos;
|
||||
}
|
||||
}
|
||||
|
||||
void Shortcut::keyReleaseEvent(QKeyEvent* event)
|
||||
{
|
||||
const int KeyMask = 0x01FFFFFF;
|
||||
|
||||
if ((mSequence[mCurrentPos] & KeyMask) == event->key())
|
||||
{
|
||||
if (mCurrentPos == mLastPos)
|
||||
{
|
||||
activated(false);
|
||||
mCurrentPos = 0; // Resets to start, maybe shouldn't?
|
||||
}
|
||||
else if (mCurrentPos > 0)
|
||||
{
|
||||
--mCurrentPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Shortcut::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
int withMod = event->button() | (int)event->modifiers();
|
||||
int noMod = event->button();
|
||||
|
||||
if (withMod == mSequence[mCurrentPos] || (mCurrentPos > 0 && noMod == mSequence[mCurrentPos]))
|
||||
{
|
||||
if (mCurrentPos == mLastPos)
|
||||
activated(true);
|
||||
else
|
||||
++mCurrentPos;
|
||||
}
|
||||
}
|
||||
|
||||
void Shortcut::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
const int MouseMask = 0x0000001F;
|
||||
|
||||
if ((mSequence[mCurrentPos] & MouseMask) == event->button())
|
||||
{
|
||||
if (mCurrentPos == mLastPos)
|
||||
{
|
||||
activated(false);
|
||||
mCurrentPos = 0;
|
||||
}
|
||||
else if (mCurrentPos > 0)
|
||||
{
|
||||
--mCurrentPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QShortcutWrapper::QShortcutWrapper(const std::string& name, QShortcut* shortcut)
|
||||
: QObject(shortcut)
|
||||
, mName(name)
|
||||
, mShortcut(shortcut)
|
||||
{
|
||||
State::get().getShortcutManager().addShortcut(this);
|
||||
setSequence(State::get().getShortcutManager().getSequence(name));
|
||||
}
|
||||
|
||||
QShortcutWrapper::~QShortcutWrapper()
|
||||
{
|
||||
State::get().getShortcutManager().removeShortcut(this);
|
||||
}
|
||||
|
||||
const std::string& QShortcutWrapper::getName() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
const QKeySequence& QShortcutWrapper::getSequence() const
|
||||
{
|
||||
return mSequence;
|
||||
}
|
||||
|
||||
void QShortcutWrapper::setSequence(const QKeySequence& sequence)
|
||||
{
|
||||
mSequence = sequence;
|
||||
mShortcut->setKey(sequence);
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
#ifndef CSM_PREFS_SHORTCUT_H
|
||||
#define CSM_PREFS_SHORTCUT_H
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QObject>
|
||||
|
||||
class QKeyEvent;
|
||||
class QMouseEvent;
|
||||
class QShortcut;
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
/// A class similar in purpose to QShortcut, but with the ability to use mouse buttons
|
||||
class Shortcut : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Shortcut(const std::string& name, QObject* parent);
|
||||
~Shortcut();
|
||||
|
||||
const std::string& getName() const;
|
||||
const QKeySequence& getSequence() const;
|
||||
|
||||
void setSequence(const QKeySequence& sequence);
|
||||
|
||||
private:
|
||||
|
||||
std::string mName;
|
||||
QKeySequence mSequence;
|
||||
int mCurrentPos;
|
||||
int mLastPos;
|
||||
|
||||
public slots:
|
||||
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
|
||||
signals:
|
||||
|
||||
/// Triggered when the shortcut is activated or deactived; can be determined from \p active
|
||||
void activated(bool active);
|
||||
};
|
||||
|
||||
/// Wraps a QShortcut object so that the sequence can be modified by the settings
|
||||
class QShortcutWrapper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
QShortcutWrapper(const std::string& name, QShortcut* shortcut);
|
||||
~QShortcutWrapper();
|
||||
|
||||
const std::string& getName() const;
|
||||
const QKeySequence& getSequence() const;
|
||||
|
||||
void setSequence(const QKeySequence& sequence);
|
||||
|
||||
private:
|
||||
|
||||
std::string mName;
|
||||
QKeySequence mSequence;
|
||||
QShortcut* mShortcut;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,197 @@
|
||||
#include "shortcutmanager.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <QMetaEnum>
|
||||
|
||||
#include "shortcut.hpp"
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
void ShortcutManager::addShortcut(Shortcut* shortcut)
|
||||
{
|
||||
mShortcuts.insert(std::make_pair(shortcut->getName(), shortcut));
|
||||
}
|
||||
|
||||
void ShortcutManager::addShortcut(QShortcutWrapper* wrapper)
|
||||
{
|
||||
mShortcutWrappers.insert(std::make_pair(wrapper->getName(), wrapper));
|
||||
}
|
||||
|
||||
void ShortcutManager::removeShortcut(Shortcut* shortcut)
|
||||
{
|
||||
std::pair<ShortcutMap::iterator, ShortcutMap::iterator> range = mShortcuts.equal_range(shortcut->getName());
|
||||
|
||||
for (ShortcutMap::iterator it = range.first; it != range.second;)
|
||||
{
|
||||
if (it->second == shortcut)
|
||||
{
|
||||
it = mShortcuts.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutManager::removeShortcut(QShortcutWrapper* wrapper)
|
||||
{
|
||||
std::pair<ShortcutWrapperMap::iterator, ShortcutWrapperMap::iterator> range = mShortcutWrappers.equal_range(
|
||||
wrapper->getName());
|
||||
|
||||
for (ShortcutWrapperMap::iterator it = range.first; it != range.second;)
|
||||
{
|
||||
if (it->second == wrapper)
|
||||
{
|
||||
it = mShortcutWrappers.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QKeySequence ShortcutManager::getSequence(const std::string& name) const
|
||||
{
|
||||
QKeySequence sequence;
|
||||
SequenceMap::const_iterator item = mSequences.find(name);
|
||||
|
||||
if (item != mSequences.end())
|
||||
{
|
||||
sequence = item->second;
|
||||
}
|
||||
|
||||
return sequence;
|
||||
}
|
||||
|
||||
void ShortcutManager::setSequence(const std::string& name, const QKeySequence& sequence)
|
||||
{
|
||||
// Add to map/modify
|
||||
SequenceMap::iterator item = mSequences.find(name);
|
||||
|
||||
if (item != mSequences.end())
|
||||
{
|
||||
item->second = sequence;
|
||||
}
|
||||
else
|
||||
{
|
||||
mSequences.insert(std::make_pair(name, sequence));
|
||||
}
|
||||
|
||||
// Change active shortcuts
|
||||
std::pair<ShortcutMap::iterator, ShortcutMap::iterator> rangeS = mShortcuts.equal_range(name);
|
||||
std::pair<ShortcutWrapperMap::iterator, ShortcutWrapperMap::iterator> rangeW = mShortcutWrappers.equal_range(name);
|
||||
|
||||
for (ShortcutMap::iterator it = rangeS.first; it != rangeS.second; ++it)
|
||||
{
|
||||
it->second->setSequence(sequence);
|
||||
}
|
||||
|
||||
for (ShortcutWrapperMap::iterator it = rangeW.first; it != rangeW.second; ++it)
|
||||
{
|
||||
it->second->setSequence(sequence);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ShortcutManager::sequenceToString(const QKeySequence& seq)
|
||||
{
|
||||
const int MouseMask = 0x0000001F; // Conflicts with key
|
||||
const int KeyMask = 0x01FFFFFF;
|
||||
const int ModMask = 0x7E000000;
|
||||
|
||||
const int KeyEnumIndex = staticQtMetaObject.indexOfEnumerator("Key");
|
||||
const int ModEnumIndex = staticQtMetaObject.indexOfEnumerator("KeyboardModifiers");
|
||||
|
||||
std::string output;
|
||||
|
||||
for (int i = 0; i < seq.count(); ++i)
|
||||
{
|
||||
if (seq[i] & ModMask)
|
||||
{
|
||||
// TODO separate out modifiers to allow more than 1
|
||||
output.append(staticQtMetaObject.enumerator(ModEnumIndex).valueToKey(seq[i] & ModMask));
|
||||
output.append("+");
|
||||
}
|
||||
|
||||
if (seq[i] & KeyMask & ~MouseMask)
|
||||
{
|
||||
// Is a key
|
||||
output.append(staticQtMetaObject.enumerator(KeyEnumIndex).valueToKey(seq[i] & KeyMask));
|
||||
output.append(",");
|
||||
}
|
||||
else if (seq[i] & MouseMask)
|
||||
{
|
||||
std::stringstream ss;
|
||||
std::string num;
|
||||
|
||||
unsigned int value = (unsigned int)(seq[i] & MouseMask);
|
||||
|
||||
// value will never be 0
|
||||
int exponent = 1; // Offset by 1
|
||||
while (value >>= 1)
|
||||
++exponent;
|
||||
|
||||
ss << exponent;
|
||||
ss >> num;
|
||||
|
||||
// Is a mouse button
|
||||
output.append("Mouse");
|
||||
output.append(num);
|
||||
output.append(",");
|
||||
}
|
||||
}
|
||||
|
||||
// Remove last comma
|
||||
if (output.size() > 0)
|
||||
{
|
||||
output.resize(output.size() - 1);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
QKeySequence ShortcutManager::stringToSequence(const std::string& input)
|
||||
{
|
||||
const int KeyEnumIndex = staticQtMetaObject.indexOfEnumerator("Key");
|
||||
const int ModEnumIndex = staticQtMetaObject.indexOfEnumerator("KeyboardModifiers");
|
||||
|
||||
int keys[4] = { 0, 0, 0, 0 };
|
||||
|
||||
QRegExp splitRX("[, ]");
|
||||
QStringList keyStrs = QString(input.c_str()).split(splitRX, QString::SkipEmptyParts);
|
||||
|
||||
for (int i = 0; i < keyStrs.size(); ++i)
|
||||
{
|
||||
QRegExp modSeparator("[+]");
|
||||
|
||||
QStringList separatedList = keyStrs[i].split(modSeparator, QString::SkipEmptyParts);
|
||||
for (int j = 0; j < separatedList.size(); ++j)
|
||||
{
|
||||
if (separatedList[j].startsWith("Mouse"))
|
||||
{
|
||||
QString num = separatedList[j].mid(5);
|
||||
if (num > 0)
|
||||
{
|
||||
keys[i] |= 1 << (num.toInt() - 1); // offset by 1
|
||||
}
|
||||
}
|
||||
else if (staticQtMetaObject.enumerator(ModEnumIndex).keyToValue(separatedList[j].toUtf8().data()) != -1)
|
||||
{
|
||||
keys[i] |= staticQtMetaObject.enumerator(ModEnumIndex).keyToValue(separatedList[j].toUtf8().data());
|
||||
}
|
||||
else if (staticQtMetaObject.enumerator(KeyEnumIndex).keyToValue(separatedList[j].toUtf8().data()) != -1)
|
||||
{
|
||||
keys[i] |= staticQtMetaObject.enumerator(KeyEnumIndex).keyToValue(separatedList[j].toUtf8().data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
std::cout << input << '.' << keys[0] << '.'<< keys[1] << '.'<< keys[2] << '.'<< keys[3] << std::endl;
|
||||
|
||||
return QKeySequence(keys[0], keys[1], keys[2], keys[3]);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
#ifndef CSM_PREFS_SHORTCUTMANAGER_H
|
||||
#define CSM_PREFS_SHORTCUTMANAGER_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QObject>
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
class Shortcut;
|
||||
class QShortcutWrapper;
|
||||
|
||||
/// Class used to track and update shortcuts/sequences
|
||||
class ShortcutManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/// The shortcut class will do this automatically
|
||||
void addShortcut(Shortcut* shortcut);
|
||||
/// The wrapper class will do this automatically
|
||||
void addShortcut(QShortcutWrapper* wrapper);
|
||||
|
||||
/// The shortcut class will do this automatically
|
||||
void removeShortcut(Shortcut* shortcut);
|
||||
/// The wrapper class will do this automatically
|
||||
void removeShortcut(QShortcutWrapper* wrapper);
|
||||
|
||||
QKeySequence getSequence(const std::string& name) const;
|
||||
void setSequence(const std::string& name, const QKeySequence& sequence);
|
||||
|
||||
std::string sequenceToString(const QKeySequence& sequence);
|
||||
QKeySequence stringToSequence(const std::string& str);
|
||||
|
||||
private:
|
||||
|
||||
// Need a multimap in case multiple shortcuts share the same name
|
||||
typedef std::multimap<std::string, Shortcut*> ShortcutMap;
|
||||
typedef std::multimap<std::string, QShortcutWrapper*> ShortcutWrapperMap;
|
||||
|
||||
typedef std::map<std::string, QKeySequence> SequenceMap;
|
||||
|
||||
ShortcutMap mShortcuts;
|
||||
ShortcutWrapperMap mShortcutWrappers;
|
||||
SequenceMap mSequences;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,43 @@
|
||||
#include "shortcutsetting.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QWidget>
|
||||
|
||||
#include "state.hpp"
|
||||
#include "shortcutmanager.hpp"
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
ShortcutSetting::ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
|
||||
const std::string& label, const QKeySequence& default_)
|
||||
: Setting(parent, values, mutex, key, label)
|
||||
, mDefault(default_)
|
||||
{
|
||||
State::get().getShortcutManager().setSequence(key, mDefault);
|
||||
}
|
||||
|
||||
std::pair<QWidget*, QWidget*> ShortcutSetting::makeWidgets(QWidget* parent)
|
||||
{
|
||||
QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);
|
||||
QLineEdit* widget = new QLineEdit(State::get().getShortcutManager().sequenceToString(mDefault).c_str(), parent);
|
||||
|
||||
connect(widget, SIGNAL(textChanged(const QString&)), this, SLOT(valueChanged(const QString&)));
|
||||
|
||||
return std::make_pair(label, widget);
|
||||
}
|
||||
|
||||
void ShortcutSetting::valueChanged(const QString& text)
|
||||
{
|
||||
{
|
||||
QMutexLocker lock(getMutex());
|
||||
getValues().setString(getKey(), getParent()->getKey(), text.toUtf8().data());
|
||||
|
||||
QKeySequence sequence = State::get().getShortcutManager().stringToSequence(text.toUtf8().data());
|
||||
|
||||
State::get().getShortcutManager().setSequence(getKey(), sequence);
|
||||
}
|
||||
|
||||
getParent()->getState()->update(*this);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
#ifndef CSM_PREFS_SHORTCUTSETTING_H
|
||||
#define CSM_PREFS_SHORTCUTSETTING_H
|
||||
|
||||
#include <QKeySequence>
|
||||
|
||||
#include "setting.hpp"
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
class ShortcutSetting : public Setting
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QKeySequence mDefault;
|
||||
|
||||
public:
|
||||
|
||||
ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
|
||||
const std::string& label, const QKeySequence& default_);
|
||||
|
||||
// TODO replace with custom page
|
||||
virtual std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent);
|
||||
|
||||
private slots:
|
||||
|
||||
void valueChanged(const QString& text);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue