You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
9.3 KiB
377 lines
9.3 KiB
#include "qhotkey.h" |
|
#include "qhotkey_p.h" |
|
#include <QCoreApplication> |
|
#include <QAbstractEventDispatcher> |
|
#include <QMetaMethod> |
|
#include <QThread> |
|
#include <QDebug> |
|
|
|
Q_LOGGING_CATEGORY(logQHotkey, "QHotkey") |
|
|
|
void QHotkey::addGlobalMapping(const QKeySequence &shortcut, QHotkey::NativeShortcut nativeShortcut) |
|
{ |
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
|
const int key = shortcut[0].toCombined(); |
|
#else |
|
const int key = shortcut[0]; |
|
#endif |
|
|
|
QMetaObject::invokeMethod(QHotkeyPrivate::instance(), "addMappingInvoked", Qt::QueuedConnection, |
|
Q_ARG(Qt::Key, Qt::Key(key & ~Qt::KeyboardModifierMask)), |
|
Q_ARG(Qt::KeyboardModifiers, Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask)), |
|
Q_ARG(QHotkey::NativeShortcut, nativeShortcut)); |
|
} |
|
|
|
bool QHotkey::isPlatformSupported() |
|
{ |
|
return QHotkeyPrivate::isPlatformSupported(); |
|
} |
|
|
|
QHotkey::QHotkey(QObject *parent) : |
|
QObject(parent), |
|
_keyCode(Qt::Key_unknown), |
|
_modifiers(Qt::NoModifier), |
|
_registered(false) |
|
{} |
|
|
|
QHotkey::QHotkey(const QKeySequence &shortcut, bool autoRegister, QObject *parent) : |
|
QHotkey(parent) |
|
{ |
|
setShortcut(shortcut, autoRegister); |
|
} |
|
|
|
QHotkey::QHotkey(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister, QObject *parent) : |
|
QHotkey(parent) |
|
{ |
|
setShortcut(keyCode, modifiers, autoRegister); |
|
} |
|
|
|
QHotkey::QHotkey(QHotkey::NativeShortcut shortcut, bool autoRegister, QObject *parent) : |
|
QHotkey(parent) |
|
{ |
|
setNativeShortcut(shortcut, autoRegister); |
|
} |
|
|
|
QHotkey::~QHotkey() |
|
{ |
|
if(_registered) |
|
QHotkeyPrivate::instance()->removeShortcut(this); |
|
} |
|
|
|
QKeySequence QHotkey::shortcut() const |
|
{ |
|
if(_keyCode == Qt::Key_unknown) |
|
return QKeySequence(); |
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
|
return QKeySequence((_keyCode | _modifiers).toCombined()); |
|
#else |
|
return QKeySequence(static_cast<int>(_keyCode | _modifiers)); |
|
#endif |
|
} |
|
|
|
Qt::Key QHotkey::keyCode() const |
|
{ |
|
return _keyCode; |
|
} |
|
|
|
Qt::KeyboardModifiers QHotkey::modifiers() const |
|
{ |
|
return _modifiers; |
|
} |
|
|
|
QHotkey::NativeShortcut QHotkey::currentNativeShortcut() const |
|
{ |
|
return _nativeShortcut; |
|
} |
|
|
|
bool QHotkey::isRegistered() const |
|
{ |
|
return _registered; |
|
} |
|
|
|
bool QHotkey::setShortcut(const QKeySequence &shortcut, bool autoRegister) |
|
{ |
|
if(shortcut.isEmpty()) |
|
return resetShortcut(); |
|
if(shortcut.count() > 1) { |
|
qCWarning(logQHotkey, "Keysequences with multiple shortcuts are not allowed! " |
|
"Only the first shortcut will be used!"); |
|
} |
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
|
const int key = shortcut[0].toCombined(); |
|
#else |
|
const int key = shortcut[0]; |
|
#endif |
|
|
|
return setShortcut(Qt::Key(key & ~Qt::KeyboardModifierMask), |
|
Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask), |
|
autoRegister); |
|
} |
|
|
|
bool QHotkey::setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister) |
|
{ |
|
if(_registered) { |
|
if(autoRegister) { |
|
if(!QHotkeyPrivate::instance()->removeShortcut(this)) |
|
return false; |
|
} else |
|
return false; |
|
} |
|
|
|
if(keyCode == Qt::Key_unknown) { |
|
_keyCode = Qt::Key_unknown; |
|
_modifiers = Qt::NoModifier; |
|
_nativeShortcut = NativeShortcut(); |
|
return true; |
|
} |
|
|
|
_keyCode = keyCode; |
|
_modifiers = modifiers; |
|
_nativeShortcut = QHotkeyPrivate::instance()->nativeShortcut(keyCode, modifiers); |
|
if(_nativeShortcut.isValid()) { |
|
if(autoRegister) |
|
return QHotkeyPrivate::instance()->addShortcut(this); |
|
return true; |
|
} |
|
|
|
qCWarning(logQHotkey) << "Unable to map shortcut to native keys. Key:" << keyCode << "Modifiers:" << modifiers; |
|
_keyCode = Qt::Key_unknown; |
|
_modifiers = Qt::NoModifier; |
|
_nativeShortcut = NativeShortcut(); |
|
return false; |
|
} |
|
|
|
bool QHotkey::resetShortcut() |
|
{ |
|
if(_registered && |
|
!QHotkeyPrivate::instance()->removeShortcut(this)) { |
|
return false; |
|
} |
|
|
|
_keyCode = Qt::Key_unknown; |
|
_modifiers = Qt::NoModifier; |
|
_nativeShortcut = NativeShortcut(); |
|
return true; |
|
} |
|
|
|
bool QHotkey::setNativeShortcut(QHotkey::NativeShortcut nativeShortcut, bool autoRegister) |
|
{ |
|
if(_registered) { |
|
if(autoRegister) { |
|
if(!QHotkeyPrivate::instance()->removeShortcut(this)) |
|
return false; |
|
} else |
|
return false; |
|
} |
|
|
|
if(nativeShortcut.isValid()) { |
|
_keyCode = Qt::Key_unknown; |
|
_modifiers = Qt::NoModifier; |
|
_nativeShortcut = nativeShortcut; |
|
if(autoRegister) |
|
return QHotkeyPrivate::instance()->addShortcut(this); |
|
return true; |
|
} |
|
|
|
_keyCode = Qt::Key_unknown; |
|
_modifiers = Qt::NoModifier; |
|
_nativeShortcut = NativeShortcut(); |
|
return true; |
|
} |
|
|
|
bool QHotkey::setRegistered(bool registered) |
|
{ |
|
if(_registered && !registered) |
|
return QHotkeyPrivate::instance()->removeShortcut(this); |
|
if(!_registered && registered) { |
|
if(!_nativeShortcut.isValid()) |
|
return false; |
|
return QHotkeyPrivate::instance()->addShortcut(this); |
|
} |
|
return true; |
|
} |
|
|
|
|
|
|
|
// ---------- QHotkeyPrivate implementation ---------- |
|
|
|
QHotkeyPrivate::QHotkeyPrivate() |
|
{ |
|
Q_ASSERT_X(qApp, Q_FUNC_INFO, "QHotkey requires QCoreApplication to be instantiated"); |
|
qApp->eventDispatcher()->installNativeEventFilter(this); |
|
} |
|
|
|
QHotkeyPrivate::~QHotkeyPrivate() |
|
{ |
|
if(!shortcuts.isEmpty()) |
|
qCWarning(logQHotkey) << "QHotkeyPrivate destroyed with registered shortcuts!"; |
|
if(qApp && qApp->eventDispatcher()) |
|
qApp->eventDispatcher()->removeNativeEventFilter(this); |
|
} |
|
|
|
QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcut(Qt::Key keycode, Qt::KeyboardModifiers modifiers) |
|
{ |
|
Qt::ConnectionType conType = (QThread::currentThread() == thread() ? |
|
Qt::DirectConnection : |
|
Qt::BlockingQueuedConnection); |
|
QHotkey::NativeShortcut res; |
|
if(!QMetaObject::invokeMethod(this, "nativeShortcutInvoked", conType, |
|
Q_RETURN_ARG(QHotkey::NativeShortcut, res), |
|
Q_ARG(Qt::Key, keycode), |
|
Q_ARG(Qt::KeyboardModifiers, modifiers))) { |
|
return QHotkey::NativeShortcut(); |
|
} |
|
return res; |
|
} |
|
|
|
bool QHotkeyPrivate::addShortcut(QHotkey *hotkey) |
|
{ |
|
if(hotkey->_registered) |
|
return false; |
|
|
|
Qt::ConnectionType conType = (QThread::currentThread() == thread() ? |
|
Qt::DirectConnection : |
|
Qt::BlockingQueuedConnection); |
|
bool res = false; |
|
if(!QMetaObject::invokeMethod(this, "addShortcutInvoked", conType, |
|
Q_RETURN_ARG(bool, res), |
|
Q_ARG(QHotkey*, hotkey))) { |
|
return false; |
|
} |
|
|
|
if(res) |
|
emit hotkey->registeredChanged(true); |
|
return res; |
|
} |
|
|
|
bool QHotkeyPrivate::removeShortcut(QHotkey *hotkey) |
|
{ |
|
if(!hotkey->_registered) |
|
return false; |
|
|
|
Qt::ConnectionType conType = (QThread::currentThread() == thread() ? |
|
Qt::DirectConnection : |
|
Qt::BlockingQueuedConnection); |
|
bool res = false; |
|
if(!QMetaObject::invokeMethod(this, "removeShortcutInvoked", conType, |
|
Q_RETURN_ARG(bool, res), |
|
Q_ARG(QHotkey*, hotkey))) { |
|
return false; |
|
} |
|
|
|
if(res) |
|
emit hotkey->registeredChanged(false); |
|
return res; |
|
} |
|
|
|
void QHotkeyPrivate::activateShortcut(QHotkey::NativeShortcut shortcut) |
|
{ |
|
QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::activated); |
|
for(QHotkey *hkey : shortcuts.values(shortcut)) |
|
signal.invoke(hkey, Qt::QueuedConnection); |
|
} |
|
|
|
void QHotkeyPrivate::releaseShortcut(QHotkey::NativeShortcut shortcut) |
|
{ |
|
QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::released); |
|
for(QHotkey *hkey : shortcuts.values(shortcut)) |
|
signal.invoke(hkey, Qt::QueuedConnection); |
|
} |
|
|
|
void QHotkeyPrivate::addMappingInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers, QHotkey::NativeShortcut nativeShortcut) |
|
{ |
|
mapping.insert({keycode, modifiers}, nativeShortcut); |
|
} |
|
|
|
bool QHotkeyPrivate::addShortcutInvoked(QHotkey *hotkey) |
|
{ |
|
QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut; |
|
|
|
if(!shortcuts.contains(shortcut)) { |
|
if(!registerShortcut(shortcut)) { |
|
qCWarning(logQHotkey) << QHotkey::tr("Failed to register %1. Error: %2").arg(hotkey->shortcut().toString(), error); |
|
return false; |
|
} |
|
} |
|
|
|
shortcuts.insert(shortcut, hotkey); |
|
hotkey->_registered = true; |
|
return true; |
|
} |
|
|
|
bool QHotkeyPrivate::removeShortcutInvoked(QHotkey *hotkey) |
|
{ |
|
QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut; |
|
|
|
if(shortcuts.remove(shortcut, hotkey) == 0) |
|
return false; |
|
hotkey->_registered = false; |
|
emit hotkey->registeredChanged(true); |
|
if(shortcuts.count(shortcut) == 0) { |
|
if (!unregisterShortcut(shortcut)) { |
|
qCWarning(logQHotkey) << QHotkey::tr("Failed to unregister %1. Error: %2").arg(hotkey->shortcut().toString(), error); |
|
return false; |
|
} |
|
return true; |
|
} |
|
return true; |
|
} |
|
|
|
QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcutInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers) |
|
{ |
|
if(mapping.contains({keycode, modifiers})) |
|
return mapping.value({keycode, modifiers}); |
|
|
|
bool ok1 = false; |
|
auto k = nativeKeycode(keycode, ok1); |
|
bool ok2 = false; |
|
auto m = nativeModifiers(modifiers, ok2); |
|
if(ok1 && ok2) |
|
return {k, m}; |
|
return {}; |
|
} |
|
|
|
|
|
|
|
QHotkey::NativeShortcut::NativeShortcut() : |
|
key(), |
|
modifier(), |
|
valid(false) |
|
{} |
|
|
|
QHotkey::NativeShortcut::NativeShortcut(quint32 key, quint32 modifier) : |
|
key(key), |
|
modifier(modifier), |
|
valid(true) |
|
{} |
|
|
|
bool QHotkey::NativeShortcut::isValid() const |
|
{ |
|
return valid; |
|
} |
|
|
|
bool QHotkey::NativeShortcut::operator ==(QHotkey::NativeShortcut other) const |
|
{ |
|
return (key == other.key) && |
|
(modifier == other.modifier) && |
|
valid == other.valid; |
|
} |
|
|
|
bool QHotkey::NativeShortcut::operator !=(QHotkey::NativeShortcut other) const |
|
{ |
|
return (key != other.key) || |
|
(modifier != other.modifier) || |
|
valid != other.valid; |
|
} |
|
|
|
QHOTKEY_HASH_SEED qHash(QHotkey::NativeShortcut key) |
|
{ |
|
return qHash(key.key) ^ qHash(key.modifier); |
|
} |
|
|
|
QHOTKEY_HASH_SEED qHash(QHotkey::NativeShortcut key, QHOTKEY_HASH_SEED seed) |
|
{ |
|
return qHash(key.key, seed) ^ qHash(key.modifier, seed); |
|
}
|
|
|