macOS: Modernize QCocoaKeyMapper key map
Change-Id: I3e8a4cfa611b6ae575466c493aac438dc831e89a Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
parent
ce9efcf4a9
commit
fab3dfff7d
@ -69,16 +69,10 @@ QT_BEGIN_NAMESPACE
|
|||||||
15. Meta + Alt + Control
|
15. Meta + Alt + Control
|
||||||
16. Meta + Alt + Control + Shift
|
16. Meta + Alt + Control + Shift
|
||||||
*/
|
*/
|
||||||
struct KeyboardLayoutItem {
|
|
||||||
quint32 qtKey[16]; // Can by any Qt::Key_<foo>, or unicode character
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class QCocoaKeyMapper
|
class QCocoaKeyMapper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QCocoaKeyMapper();
|
|
||||||
~QCocoaKeyMapper();
|
|
||||||
static Qt::KeyboardModifiers queryKeyboardModifiers();
|
static Qt::KeyboardModifiers queryKeyboardModifiers();
|
||||||
QList<int> possibleKeys(const QKeyEvent *event) const;
|
QList<int> possibleKeys(const QKeyEvent *event) const;
|
||||||
|
|
||||||
@ -89,9 +83,18 @@ public:
|
|||||||
static Qt::Key fromCocoaKey(QChar keyCode);
|
static Qt::Key fromCocoaKey(QChar keyCode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using VirtualKeyCode = unsigned short;
|
||||||
|
struct KeyMap : std::array<char32_t, 16>
|
||||||
|
{
|
||||||
|
// Initialize first element to a sentinel that allows us
|
||||||
|
// to distinguish an uninitialized map from an initialized.
|
||||||
|
// Using 0 would not allow us to map U+0000 (NUL), however
|
||||||
|
// unlikely that is.
|
||||||
|
KeyMap() : std::array<char32_t, 16>{Qt::Key_unknown} {}
|
||||||
|
};
|
||||||
|
|
||||||
bool updateKeyboard();
|
bool updateKeyboard();
|
||||||
void deleteLayouts();
|
const KeyMap &keyMapForKey(VirtualKeyCode virtualKey, QChar unicodeKey) const;
|
||||||
KeyboardLayoutItem *keyMapForKey(unsigned short macVirtualKey, QChar unicodeKey) const;
|
|
||||||
|
|
||||||
QCFType<TISInputSourceRef> m_currentInputSource = nullptr;
|
QCFType<TISInputSourceRef> m_currentInputSource = nullptr;
|
||||||
|
|
||||||
@ -99,7 +102,8 @@ private:
|
|||||||
const UCKeyboardLayout *m_keyboardLayoutFormat = nullptr;
|
const UCKeyboardLayout *m_keyboardLayoutFormat = nullptr;
|
||||||
KeyboardLayoutKind m_keyboardKind = kKLKCHRuchrKind;
|
KeyboardLayoutKind m_keyboardKind = kKLKCHRuchrKind;
|
||||||
mutable UInt32 m_deadKeyState = 0; // Maintains dead key state beween calls to UCKeyTranslate
|
mutable UInt32 m_deadKeyState = 0; // Maintains dead key state beween calls to UCKeyTranslate
|
||||||
mutable KeyboardLayoutItem *m_keyLayout[256];
|
|
||||||
|
mutable QHash<VirtualKeyCode, KeyMap> m_keyMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -381,16 +381,6 @@ Qt::Key QCocoaKeyMapper::fromCocoaKey(QChar keyCode)
|
|||||||
|
|
||||||
// ------------------------------------------------
|
// ------------------------------------------------
|
||||||
|
|
||||||
QCocoaKeyMapper::QCocoaKeyMapper()
|
|
||||||
{
|
|
||||||
memset(m_keyLayout, 0, sizeof(m_keyLayout));
|
|
||||||
}
|
|
||||||
|
|
||||||
QCocoaKeyMapper::~QCocoaKeyMapper()
|
|
||||||
{
|
|
||||||
deleteLayouts();
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::KeyboardModifiers QCocoaKeyMapper::queryKeyboardModifiers()
|
Qt::KeyboardModifiers QCocoaKeyMapper::queryKeyboardModifiers()
|
||||||
{
|
{
|
||||||
return fromCocoaModifiers(NSEvent.modifierFlags);
|
return fromCocoaModifiers(NSEvent.modifierFlags);
|
||||||
@ -410,7 +400,7 @@ bool QCocoaKeyMapper::updateKeyboard()
|
|||||||
m_keyboardKind = LMGetKbdType();
|
m_keyboardKind = LMGetKbdType();
|
||||||
m_deadKeyState = 0;
|
m_deadKeyState = 0;
|
||||||
|
|
||||||
deleteLayouts();
|
m_keyMap.clear();
|
||||||
|
|
||||||
if (auto data = CFDataRef(TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData))) {
|
if (auto data = CFDataRef(TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData))) {
|
||||||
const UCKeyboardLayout *uchrData = reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data));
|
const UCKeyboardLayout *uchrData = reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data));
|
||||||
@ -429,17 +419,6 @@ bool QCocoaKeyMapper::updateKeyboard()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QCocoaKeyMapper::deleteLayouts()
|
|
||||||
{
|
|
||||||
m_keyboardMode = NullMode;
|
|
||||||
for (int i = 0; i < 255; ++i) {
|
|
||||||
if (m_keyLayout[i]) {
|
|
||||||
delete m_keyLayout[i];
|
|
||||||
m_keyLayout[i] = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr Qt::KeyboardModifiers modifierCombinations[] = {
|
static constexpr Qt::KeyboardModifiers modifierCombinations[] = {
|
||||||
Qt::NoModifier, // 0
|
Qt::NoModifier, // 0
|
||||||
Qt::ShiftModifier, // 1
|
Qt::ShiftModifier, // 1
|
||||||
@ -460,31 +439,31 @@ static constexpr Qt::KeyboardModifiers modifierCombinations[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Returns a key map for the given \macVirtualKey based on all
|
Returns a key map for the given \virtualKey based on all
|
||||||
possible modifier combinations.
|
possible modifier combinations.
|
||||||
*/
|
*/
|
||||||
KeyboardLayoutItem *QCocoaKeyMapper::keyMapForKey(unsigned short macVirtualKey, QChar unicodeKey) const
|
const QCocoaKeyMapper::KeyMap &QCocoaKeyMapper::keyMapForKey(VirtualKeyCode virtualKey, QChar unicodeKey) const
|
||||||
{
|
{
|
||||||
const_cast<QCocoaKeyMapper *>(this)->updateKeyboard();
|
const_cast<QCocoaKeyMapper *>(this)->updateKeyboard();
|
||||||
|
|
||||||
Q_ASSERT(macVirtualKey < 256);
|
auto &keyMap = m_keyMap[virtualKey];
|
||||||
if (auto *existingKeyMap = m_keyLayout[macVirtualKey])
|
if (keyMap[Qt::NoModifier] != Qt::Key_unknown)
|
||||||
return existingKeyMap;
|
return keyMap; // Already filled
|
||||||
|
|
||||||
qCDebug(lcQpaKeyMapper, "Updating key map for virtual key = 0x%02x!", (uint)macVirtualKey);
|
qCDebug(lcQpaKeyMapper, "Updating key map for virtual key = 0x%02x!", (uint)virtualKey);
|
||||||
|
|
||||||
UniCharCount maxStringLength = 10;
|
UniCharCount maxStringLength = 10;
|
||||||
UniChar unicodeString[maxStringLength];
|
UniChar unicodeString[maxStringLength];
|
||||||
m_keyLayout[macVirtualKey] = new KeyboardLayoutItem;
|
|
||||||
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
UniCharCount actualStringLength = 0;
|
Q_ASSERT(!i || keyMap[i] == 0);
|
||||||
m_keyLayout[macVirtualKey]->qtKey[i] = 0;
|
|
||||||
|
|
||||||
auto qtModifiers = modifierCombinations[i];
|
auto qtModifiers = modifierCombinations[i];
|
||||||
auto carbonModifiers = toCarbonModifiers(qtModifiers);
|
auto carbonModifiers = toCarbonModifiers(qtModifiers);
|
||||||
const UInt32 modifierKeyState = (carbonModifiers >> 8) & 0xFF;
|
const UInt32 modifierKeyState = (carbonModifiers >> 8) & 0xFF;
|
||||||
OSStatus err = UCKeyTranslate(m_keyboardLayoutFormat, macVirtualKey,
|
|
||||||
|
UniCharCount actualStringLength = 0;
|
||||||
|
OSStatus err = UCKeyTranslate(m_keyboardLayoutFormat, virtualKey,
|
||||||
kUCKeyActionDown, modifierKeyState, m_keyboardKind, OptionBits(0),
|
kUCKeyActionDown, modifierKeyState, m_keyboardKind, OptionBits(0),
|
||||||
&m_deadKeyState, maxStringLength, &actualStringLength, unicodeString);
|
&m_deadKeyState, maxStringLength, &actualStringLength, unicodeString);
|
||||||
|
|
||||||
@ -492,36 +471,36 @@ KeyboardLayoutItem *QCocoaKeyMapper::keyMapForKey(unsigned short macVirtualKey,
|
|||||||
if (err == noErr && actualStringLength)
|
if (err == noErr && actualStringLength)
|
||||||
unicodeKey = QChar(unicodeString[0]);
|
unicodeKey = QChar(unicodeString[0]);
|
||||||
|
|
||||||
int qtkey = toKeyCode(unicodeKey, macVirtualKey, qtModifiers);
|
int qtkey = toKeyCode(unicodeKey, virtualKey, qtModifiers);
|
||||||
if (qtkey == Qt::Key_unknown)
|
if (qtkey == Qt::Key_unknown)
|
||||||
qtkey = unicodeKey.unicode();
|
qtkey = unicodeKey.unicode();
|
||||||
|
|
||||||
m_keyLayout[macVirtualKey]->qtKey[i] = qtkey;
|
keyMap[i] = qtkey;
|
||||||
|
|
||||||
qCDebug(lcQpaKeyMapper, " [%d] (%d,0x%02x,'%c')", i, qtkey, qtkey, qtkey);
|
qCDebug(lcQpaKeyMapper, " [%d] (%d,0x%02x,'%c')", i, qtkey, qtkey, qtkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_keyLayout[macVirtualKey];
|
return keyMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<int> QCocoaKeyMapper::possibleKeys(const QKeyEvent *event) const
|
QList<int> QCocoaKeyMapper::possibleKeys(const QKeyEvent *event) const
|
||||||
{
|
{
|
||||||
QList<int> ret;
|
QList<int> ret;
|
||||||
|
|
||||||
auto *keyMap = keyMapForKey(event->nativeVirtualKey(), QChar(event->key()));
|
auto keyMap = keyMapForKey(event->nativeVirtualKey(), QChar(event->key()));
|
||||||
Q_ASSERT(keyMap);
|
|
||||||
|
auto unmodifiedKey = keyMap[Qt::NoModifier];
|
||||||
|
Q_ASSERT(unmodifiedKey != Qt::Key_unknown);
|
||||||
|
|
||||||
int baseKey = keyMap->qtKey[Qt::NoModifier];
|
|
||||||
auto eventModifiers = event->modifiers();
|
auto eventModifiers = event->modifiers();
|
||||||
|
|
||||||
// The base key is always valid
|
// The base key, with the complete set of modifiers,
|
||||||
ret << int(baseKey + eventModifiers);
|
// is always valid, and the first priority.
|
||||||
|
ret << int(unmodifiedKey + eventModifiers);
|
||||||
|
|
||||||
for (int i = 1; i < 8; ++i) {
|
for (int i = 1; i < 8; ++i) {
|
||||||
int keyAfterApplyingModifiers = keyMap->qtKey[i];
|
auto keyAfterApplyingModifiers = keyMap[i];
|
||||||
if (!keyAfterApplyingModifiers)
|
if (keyAfterApplyingModifiers == unmodifiedKey)
|
||||||
continue;
|
|
||||||
if (keyAfterApplyingModifiers == baseKey)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Include key if event modifiers includes, or matches
|
// Include key if event modifiers includes, or matches
|
||||||
|
Loading…
x
Reference in New Issue
Block a user