diff options
author | Mikolaj Boc <mikolaj.boc@qt.io> | 2023-02-10 15:06:36 +0100 |
---|---|---|
committer | Mikolaj Boc <mikolaj.boc@qt.io> | 2023-03-28 17:44:28 +0100 |
commit | d7a5cb8edd4545824c6f421ea5a2557768f5834c (patch) | |
tree | d80178df7d85018be83d2bf235402076c3078789 | |
parent | 369f0754712ecebe47f97b083da47fea90af09fc (diff) | |
download | qtbase-d7a5cb8edd4545824c6f421ea5a2557768f5834c.tar.gz |
Fix the handling of dead keys and test the mechanism
An effort has been made to support more keyboard layouts that provide
dead keys (US international, Mac-specific dead keys).
The dead key is translated now at the event conversion phase, not
when it is actually used for modifying keys, which simplifies the logic.
Unittests have been created to check the translation mechanism.
Fixes: QTBUG-86272
Change-Id: I07f7d63f5a37f8469c693b034b400da99379f519
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
(cherry picked from commit 3dc6fdc6d8ae661a6800c14a2461f456ec344e17)
Reviewed-by: Mikołaj Boc <Mikolaj.Boc@qt.io>
-rw-r--r-- | src/plugins/platforms/wasm/qwasmevent.cpp | 57 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmkeytranslator.cpp | 190 | ||||
-rw-r--r-- | tests/auto/wasm/CMakeLists.txt | 11 | ||||
-rw-r--r-- | tests/auto/wasm/tst_qwasmkeytranslator.cpp | 425 |
4 files changed, 602 insertions, 81 deletions
diff --git a/src/plugins/platforms/wasm/qwasmevent.cpp b/src/plugins/platforms/wasm/qwasmevent.cpp index d6f254bc9f..6353cce48f 100644 --- a/src/plugins/platforms/wasm/qwasmevent.cpp +++ b/src/plugins/platforms/wasm/qwasmevent.cpp @@ -7,6 +7,7 @@ #include <QtCore/private/qmakearray_p.h> #include <QtCore/private/qstringiterator_p.h> +#include <QtCore/qregularexpression.h> QT_BEGIN_NAMESPACE @@ -18,19 +19,57 @@ bool isDeadKeyEvent(const char *key) return qstrncmp(key, WebDeadKeyValue.data(), WebDeadKeyValue.size()) == 0; } -Qt::Key webKeyToQtKey(const std::string &code, const std::string &key, bool isDeadKey) +Qt::Key getKeyFromCode(const std::string &code) { - if (isDeadKey) { - if (auto mapping = QWasmKeyTranslator::mapWebKeyTextToQtKey(code.c_str())) - return *mapping; - } - if (auto mapping = QWasmKeyTranslator::mapWebKeyTextToQtKey(key.c_str())) + if (auto mapping = QWasmKeyTranslator::mapWebKeyTextToQtKey(code.c_str())) return *mapping; - if (isDeadKey) + + static QRegularExpression regex(QString(QStringLiteral(R"re((?:Key|Digit)(\w))re"))); + const auto codeQString = QString::fromStdString(code); + const auto match = regex.match(codeQString); + + if (!match.hasMatch()) return Qt::Key_unknown; + constexpr size_t CharacterIndex = 1; + return static_cast<Qt::Key>(match.capturedView(CharacterIndex).at(0).toLatin1()); +} + +Qt::Key webKeyToQtKey(const std::string &code, const std::string &key, bool isDeadKey, + QFlags<Qt::KeyboardModifier> modifiers) +{ + if (isDeadKey) { + auto mapped = getKeyFromCode(code); + switch (mapped) { + case Qt::Key_U: + return Qt::Key_Dead_Diaeresis; + case Qt::Key_E: + return Qt::Key_Dead_Acute; + case Qt::Key_I: + return Qt::Key_Dead_Circumflex; + case Qt::Key_N: + return Qt::Key_Dead_Tilde; + case Qt::Key_QuoteLeft: + return modifiers.testFlag(Qt::ShiftModifier) ? Qt::Key_Dead_Tilde : Qt::Key_Dead_Grave; + case Qt::Key_6: + return Qt::Key_Dead_Circumflex; + case Qt::Key_Apostrophe: + return modifiers.testFlag(Qt::ShiftModifier) ? Qt::Key_Dead_Diaeresis + : Qt::Key_Dead_Acute; + case Qt::Key_AsciiTilde: + return Qt::Key_Dead_Tilde; + default: + return Qt::Key_unknown; + } + } else if (auto mapping = QWasmKeyTranslator::mapWebKeyTextToQtKey(key.c_str())) { + return *mapping; + } + // cast to unicode key QString str = QString::fromUtf8(key.c_str()).toUpper(); + if (str.length() > 1) + return Qt::Key_unknown; + QStringIterator i(str); return static_cast<Qt::Key>(i.next(0)); } @@ -65,9 +104,9 @@ KeyEvent::KeyEvent(EventType type, emscripten::val event) : Event(type, event["t const auto webKey = event["key"].as<std::string>(); deadKey = isDeadKeyEvent(webKey.c_str()); - key = webKeyToQtKey(code, webKey, deadKey); - modifiers = KeyboardModifier::getForEvent(event); + key = webKeyToQtKey(code, webKey, deadKey, modifiers); + text = QString::fromUtf8(webKey); if (text.size() > 1) text.clear(); diff --git a/src/plugins/platforms/wasm/qwasmkeytranslator.cpp b/src/plugins/platforms/wasm/qwasmkeytranslator.cpp index 0052495b89..8f5240d2d0 100644 --- a/src/plugins/platforms/wasm/qwasmkeytranslator.cpp +++ b/src/plugins/platforms/wasm/qwasmkeytranslator.cpp @@ -52,34 +52,17 @@ template<unsigned int Qt, char... WebChar> constexpr char Web2Qt<Qt, WebChar...>::storage[]; static constexpr const auto WebToQtKeyCodeMappings = qMakeArray( - QSortedData<Web2Qt<Qt::Key_Escape, 'E', 's', 'c', 'a', 'p', 'e'>, - Web2Qt<Qt::Key_Tab, 'T', 'a', 'b'>, + QSortedData<Web2Qt<Qt::Key_Alt, 'A', 'l', 't', 'L', 'e', 'f', 't'>, + Web2Qt<Qt::Key_Alt, 'A', 'l', 't'>, + Web2Qt<Qt::Key_AltGr, 'A', 'l', 't', 'R', 'i', 'g', 'h', 't'>, + Web2Qt<Qt::Key_Apostrophe, 'Q', 'u', 'o', 't', 'e'>, Web2Qt<Qt::Key_Backspace, 'B', 'a', 'c', 'k', 's', 'p', 'a', 'c', 'e'>, - Web2Qt<Qt::Key_Return, 'E', 'n', 't', 'e', 'r'>, - Web2Qt<Qt::Key_Insert, 'I', 'n', 's', 'e', 'r', 't'>, + Web2Qt<Qt::Key_CapsLock, 'C', 'a', 'p', 's', 'L', 'o', 'c', 'k'>, + Web2Qt<Qt::Key_Control, 'C', 'o', 'n', 't', 'r', 'o', 'l'>, Web2Qt<Qt::Key_Delete, 'D', 'e', 'l', 'e', 't', 'e'>, - Web2Qt<Qt::Key_Pause, 'P', 'a', 'u', 's', 'e'>, - Web2Qt<Qt::Key_Pause, 'C', 'l', 'e', 'a', 'r'>, - Web2Qt<Qt::Key_Home, 'H', 'o', 'm', 'e'>, Web2Qt<Qt::Key_End, 'E', 'n', 'd'>, - Web2Qt<Qt::Key_Left, 'A', 'r', 'r', 'o', 'w', 'L', 'e', 'f', 't'>, - Web2Qt<Qt::Key_Up, 'A', 'r', 'r', 'o', 'w', 'U', 'p'>, - Web2Qt<Qt::Key_Right, 'A', 'r', 'r', 'o', 'w', 'R', 'i', 'g', 'h', 't'>, Web2Qt<Qt::Key_Down, 'A', 'r', 'r', 'o', 'w', 'D', 'o', 'w', 'n'>, - Web2Qt<Qt::Key_PageUp, 'P', 'a', 'g', 'e', 'U', 'p'>, - Web2Qt<Qt::Key_PageDown, 'P', 'a', 'g', 'e', 'D', 'o', 'w', 'n'>, - Web2Qt<Qt::Key_Shift, 'S', 'h', 'i', 'f', 't'>, - Web2Qt<Qt::Key_Control, 'C', 'o', 'n', 't', 'r', 'o', 'l'>, - Web2Qt<Qt::Key_Meta, 'M', 'e', 't', 'a'>, Web2Qt<Qt::Key_Meta, 'O', 'S'>, - Web2Qt<Qt::Key_Alt, 'A', 'l', 't', 'L', 'e', 'f', 't'>, - Web2Qt<Qt::Key_Alt, 'A', 'l', 't'>, - Web2Qt<Qt::Key_CapsLock, 'C', 'a', 'p', 's', 'L', 'o', 'c', 'k'>, - Web2Qt<Qt::Key_NumLock, 'N', 'u', 'm', 'L', 'o', 'c', 'k'>, - Web2Qt<Qt::Key_ScrollLock, 'S', 'c', 'r', 'o', 'l', 'l', 'L', 'o', 'c', 'k'>, + Web2Qt<Qt::Key_Escape, 'E', 's', 'c', 'a', 'p', 'e'>, Web2Qt<Qt::Key_F1, 'F', '1'>, Web2Qt<Qt::Key_F2, 'F', '2'>, - Web2Qt<Qt::Key_F3, 'F', '3'>, Web2Qt<Qt::Key_F4, 'F', '4'>, - Web2Qt<Qt::Key_F5, 'F', '5'>, Web2Qt<Qt::Key_F6, 'F', '6'>, - Web2Qt<Qt::Key_F7, 'F', '7'>, Web2Qt<Qt::Key_F8, 'F', '8'>, - Web2Qt<Qt::Key_F9, 'F', '9'>, Web2Qt<Qt::Key_F10, 'F', '1', '0'>, Web2Qt<Qt::Key_F11, 'F', '1', '1'>, Web2Qt<Qt::Key_F12, 'F', '1', '2'>, Web2Qt<Qt::Key_F13, 'F', '1', '3'>, Web2Qt<Qt::Key_F14, 'F', '1', '4'>, Web2Qt<Qt::Key_F15, 'F', '1', '5'>, Web2Qt<Qt::Key_F16, 'F', '1', '6'>, @@ -87,34 +70,99 @@ static constexpr const auto WebToQtKeyCodeMappings = qMakeArray( Web2Qt<Qt::Key_F19, 'F', '1', '9'>, Web2Qt<Qt::Key_F20, 'F', '2', '0'>, Web2Qt<Qt::Key_F21, 'F', '2', '1'>, Web2Qt<Qt::Key_F22, 'F', '2', '2'>, Web2Qt<Qt::Key_F23, 'F', '2', '3'>, - Web2Qt<Qt::Key_Paste, 'P', 'a', 's', 't', 'e'>, - Web2Qt<Qt::Key_AltGr, 'A', 'l', 't', 'R', 'i', 'g', 'h', 't'>, + Web2Qt<Qt::Key_F3, 'F', '3'>, Web2Qt<Qt::Key_F4, 'F', '4'>, + Web2Qt<Qt::Key_F5, 'F', '5'>, Web2Qt<Qt::Key_F6, 'F', '6'>, + Web2Qt<Qt::Key_F7, 'F', '7'>, Web2Qt<Qt::Key_F8, 'F', '8'>, + Web2Qt<Qt::Key_F9, 'F', '9'>, Web2Qt<Qt::Key_F10, 'F', '1', '0'>, Web2Qt<Qt::Key_Help, 'H', 'e', 'l', 'p'>, - Web2Qt<Qt::Key_yen, 'I', 'n', 't', 'l', 'Y', 'e', 'n'>, - Web2Qt<Qt::Key_Menu, 'C', 'o', 'n', 't', 'e', 'x', 't', 'M', 'e', 'n', - 'u'>>::Data{}); + Web2Qt<Qt::Key_Home, 'H', 'o', 'm', 'e'>, Web2Qt<Qt::Key_End, 'E', 'n', 'd'>, + Web2Qt<Qt::Key_Insert, 'I', 'n', 's', 'e', 'r', 't'>, + Web2Qt<Qt::Key_Left, 'A', 'r', 'r', 'o', 'w', 'L', 'e', 'f', 't'>, + Web2Qt<Qt::Key_Meta, 'M', 'e', 't', 'a'>, Web2Qt<Qt::Key_Meta, 'O', 'S'>, + Web2Qt<Qt::Key_Menu, 'C', 'o', 'n', 't', 'e', 'x', 't', 'M', 'e', 'n', 'u'>, + Web2Qt<Qt::Key_NumLock, 'N', 'u', 'm', 'L', 'o', 'c', 'k'>, + Web2Qt<Qt::Key_PageDown, 'P', 'a', 'g', 'e', 'D', 'o', 'w', 'n'>, + Web2Qt<Qt::Key_PageUp, 'P', 'a', 'g', 'e', 'U', 'p'>, + Web2Qt<Qt::Key_Paste, 'P', 'a', 's', 't', 'e'>, + Web2Qt<Qt::Key_Pause, 'C', 'l', 'e', 'a', 'r'>, + Web2Qt<Qt::Key_Pause, 'P', 'a', 'u', 's', 'e'>, + Web2Qt<Qt::Key_QuoteLeft, 'B', 'a', 'c', 'k', 'q', 'u', 'o', 't', 'e'>, + Web2Qt<Qt::Key_QuoteLeft, 'I', 'n', 't', 'l', 'B', 'a', 'c', 'k', 's', 'l', 'a', 's', 'h'>, + Web2Qt<Qt::Key_Return, 'E', 'n', 't', 'e', 'r'>, + Web2Qt<Qt::Key_Right, 'A', 'r', 'r', 'o', 'w', 'R', 'i', 'g', 'h', 't'>, + Web2Qt<Qt::Key_ScrollLock, 'S', 'c', 'r', 'o', 'l', 'l', 'L', 'o', 'c', 'k'>, + Web2Qt<Qt::Key_Shift, 'S', 'h', 'i', 'f', 't'>, + Web2Qt<Qt::Key_Tab, 'T', 'a', 'b'>, + Web2Qt<Qt::Key_Up, 'A', 'r', 'r', 'o', 'w', 'U', 'p'>, + Web2Qt<Qt::Key_yen, 'I', 'n', 't', 'l', 'Y', 'e', 'n'>>::Data{}); + +static constexpr const auto DiacriticalCharsKeyToTextLowercase = qMakeArray( + QSortedData< + Web2Qt<Qt::Key_Aacute, '\xc3', '\xa1'>, + Web2Qt<Qt::Key_Acircumflex, '\xc3', '\xa2'>, + Web2Qt<Qt::Key_Adiaeresis, '\xc3', '\xa4'>, + Web2Qt<Qt::Key_AE, '\xc3', '\xa6'>, + Web2Qt<Qt::Key_Agrave, '\xc3', '\xa0'>, + Web2Qt<Qt::Key_Aring, '\xc3', '\xa5'>, + Web2Qt<Qt::Key_Atilde, '\xc3', '\xa3'>, + Web2Qt<Qt::Key_Ccedilla, '\xc3', '\xa7'>, + Web2Qt<Qt::Key_Eacute, '\xc3', '\xa9'>, + Web2Qt<Qt::Key_Ecircumflex, '\xc3', '\xaa'>, + Web2Qt<Qt::Key_Ediaeresis, '\xc3', '\xab'>, + Web2Qt<Qt::Key_Egrave, '\xc3', '\xa8'>, + Web2Qt<Qt::Key_Iacute, '\xc3', '\xad'>, + Web2Qt<Qt::Key_Icircumflex, '\xc3', '\xae'>, + Web2Qt<Qt::Key_Idiaeresis, '\xc3', '\xaf'>, + Web2Qt<Qt::Key_Igrave, '\xc3', '\xac'>, + Web2Qt<Qt::Key_Ntilde, '\xc3', '\xb1'>, + Web2Qt<Qt::Key_Oacute, '\xc3', '\xb3'>, + Web2Qt<Qt::Key_Ocircumflex, '\xc3', '\xb4'>, + Web2Qt<Qt::Key_Odiaeresis, '\xc3', '\xb6'>, + Web2Qt<Qt::Key_Ograve, '\xc3', '\xb2'>, + Web2Qt<Qt::Key_Ooblique, '\xc3', '\xb8'>, + Web2Qt<Qt::Key_Otilde, '\xc3', '\xb5'>, + Web2Qt<Qt::Key_Uacute, '\xc3', '\xba'>, + Web2Qt<Qt::Key_Ucircumflex, '\xc3', '\xbb'>, + Web2Qt<Qt::Key_Udiaeresis, '\xc3', '\xbc'>, + Web2Qt<Qt::Key_Ugrave, '\xc3', '\xb9'>, + Web2Qt<Qt::Key_Yacute, '\xc3', '\xbd'>, + Web2Qt<Qt::Key_ydiaeresis, '\xc3', '\xbf'>>::Data{}); -static constexpr const auto WebToQtKeyCodeMappingsWithShift = qMakeArray( +static constexpr const auto DiacriticalCharsKeyToTextUppercase = qMakeArray( QSortedData< - // shifted - Web2Qt<Qt::Key_Agrave, '\xc3', '\x80'>, Web2Qt<Qt::Key_Aacute, '\xc3', '\x81'>, + Web2Qt<Qt::Key_Aacute, '\xc3', '\x81'>, Web2Qt<Qt::Key_Acircumflex, '\xc3', '\x82'>, - Web2Qt<Qt::Key_Adiaeresis, '\xc3', '\x84'>, Web2Qt<Qt::Key_AE, '\xc3', '\x86'>, - Web2Qt<Qt::Key_Atilde, '\xc3', '\x83'>, Web2Qt<Qt::Key_Aring, '\xc3', '\x85'>, - Web2Qt<Qt::Key_Egrave, '\xc3', '\x88'>, Web2Qt<Qt::Key_Eacute, '\xc3', '\x89'>, + Web2Qt<Qt::Key_Adiaeresis, '\xc3', '\x84'>, + Web2Qt<Qt::Key_AE, '\xc3', '\x86'>, + Web2Qt<Qt::Key_Agrave, '\xc3', '\x80'>, + Web2Qt<Qt::Key_Aring, '\xc3', '\x85'>, + Web2Qt<Qt::Key_Atilde, '\xc3', '\x83'>, + Web2Qt<Qt::Key_Ccedilla, '\xc3', '\x87'>, + Web2Qt<Qt::Key_Eacute, '\xc3', '\x89'>, Web2Qt<Qt::Key_Ecircumflex, '\xc3', '\x8a'>, - Web2Qt<Qt::Key_Ediaeresis, '\xc3', '\x8b'>, Web2Qt<Qt::Key_Iacute, '\xc3', '\x8d'>, + Web2Qt<Qt::Key_Ediaeresis, '\xc3', '\x8b'>, + Web2Qt<Qt::Key_Egrave, '\xc3', '\x88'>, + Web2Qt<Qt::Key_Iacute, '\xc3', '\x8d'>, Web2Qt<Qt::Key_Icircumflex, '\xc3', '\x8e'>, - Web2Qt<Qt::Key_Idiaeresis, '\xc3', '\x8f'>, Web2Qt<Qt::Key_Igrave, '\xc3', '\x8c'>, + Web2Qt<Qt::Key_Idiaeresis, '\xc3', '\x8f'>, + Web2Qt<Qt::Key_Igrave, '\xc3', '\x8c'>, + Web2Qt<Qt::Key_Ntilde, '\xc3', '\x91'>, + Web2Qt<Qt::Key_Oacute, '\xc3', '\x93'>, Web2Qt<Qt::Key_Ocircumflex, '\xc3', '\x94'>, - Web2Qt<Qt::Key_Odiaeresis, '\xc3', '\x96'>, Web2Qt<Qt::Key_Ograve, '\xc3', '\x92'>, - Web2Qt<Qt::Key_Oacute, '\xc3', '\x93'>, Web2Qt<Qt::Key_Ooblique, '\xc3', '\x98'>, - Web2Qt<Qt::Key_Otilde, '\xc3', '\x95'>, Web2Qt<Qt::Key_Ucircumflex, '\xc3', '\x9b'>, - Web2Qt<Qt::Key_Udiaeresis, '\xc3', '\x9c'>, Web2Qt<Qt::Key_Ugrave, '\xc3', '\x99'>, - Web2Qt<Qt::Key_Uacute, '\xc3', '\x9a'>, Web2Qt<Qt::Key_Ntilde, '\xc3', '\x91'>, - Web2Qt<Qt::Key_Ccedilla, '\xc3', '\x87'>, - Web2Qt<Qt::Key_ydiaeresis, '\xc3', '\x8f'>, - Web2Qt<Qt::Key_Yacute, '\xc3', '\x9d'>>::Data{}); + Web2Qt<Qt::Key_Odiaeresis, '\xc3', '\x96'>, + Web2Qt<Qt::Key_Ograve, '\xc3', '\x92'>, + Web2Qt<Qt::Key_Ooblique, '\xc3', '\x98'>, + Web2Qt<Qt::Key_Otilde, '\xc3', '\x95'>, + Web2Qt<Qt::Key_Uacute, '\xc3', '\x9a'>, + Web2Qt<Qt::Key_Ucircumflex, '\xc3', '\x9b'>, + Web2Qt<Qt::Key_Udiaeresis, '\xc3', '\x9c'>, + Web2Qt<Qt::Key_Ugrave, '\xc3', '\x99'>, + Web2Qt<Qt::Key_Yacute, '\xc3', '\x9d'>, + Web2Qt<Qt::Key_ydiaeresis, '\xc5', '\xb8'>>::Data{}); + +static_assert(DiacriticalCharsKeyToTextLowercase.size() + == DiacriticalCharsKeyToTextUppercase.size(), + "Add the new key to both arrays"); struct KeyMapping { @@ -169,23 +217,15 @@ static Qt::Key find(const KeyMapping (&map)[N], Qt::Key key) noexcept Qt::Key translateBaseKeyUsingDeadKey(Qt::Key accentBaseKey, Qt::Key deadKey) { switch (deadKey) { - case Qt::Key_QuoteLeft: { - // ` macOS: Key_Dead_Grave - return platform() == Platform::MacOS ? find(graveKeyTable, accentBaseKey) - : find(diaeresisKeyTable, accentBaseKey); - } - case Qt::Key_O: // ´ Key_Dead_Grave + case Qt::Key_Dead_Grave: return find(graveKeyTable, accentBaseKey); - case Qt::Key_E: // ´ Key_Dead_Acute + case Qt::Key_Dead_Acute: return find(acuteKeyTable, accentBaseKey); - case Qt::Key_AsciiTilde: - case Qt::Key_N: // Key_Dead_Tilde + case Qt::Key_Dead_Tilde: return find(tildeKeyTable, accentBaseKey); - case Qt::Key_U: // ¨ Key_Dead_Diaeresis + case Qt::Key_Dead_Diaeresis: return find(diaeresisKeyTable, accentBaseKey); - case Qt::Key_I: // macOS Key_Dead_Circumflex - case Qt::Key_6: // linux - case Qt::Key_Apostrophe: // linux + case Qt::Key_Dead_Circumflex: return find(circumflexKeyTable, accentBaseKey); default: return Qt::Key_unknown; @@ -216,32 +256,38 @@ QWasmDeadKeySupport::~QWasmDeadKeySupport() = default; void QWasmDeadKeySupport::applyDeadKeyTranslations(KeyEvent *event) { - if (event->deadKey || event->key == Qt::Key_AltGr) { - if (event->modifiers.testFlag(Qt::ShiftModifier) && event->key == Qt::Key_QuoteLeft) - event->key = Qt::Key_AsciiTilde; + if (event->deadKey) { m_activeDeadKey = event->key; } else if (m_activeDeadKey != Qt::Key_unknown - && (m_keyModifiedByDeadKeyOnPress == Qt::Key_unknown - || m_keyModifiedByDeadKeyOnPress == event->key)) { + && (((m_keyModifiedByDeadKeyOnPress == Qt::Key_unknown + && event->type == EventType::KeyDown)) + || (m_keyModifiedByDeadKeyOnPress == event->key + && event->type == EventType::KeyUp))) { const Qt::Key baseKey = event->key; const Qt::Key translatedKey = translateBaseKeyUsingDeadKey(baseKey, m_activeDeadKey); - if (translatedKey != Qt::Key_unknown) + if (translatedKey != Qt::Key_unknown) { event->key = translatedKey; - if (auto foundText = event->modifiers.testFlag(Qt::ShiftModifier) - ? findKeyTextByKeyId(WebToQtKeyCodeMappingsWithShift, event->key) - : findKeyTextByKeyId(WebToQtKeyCodeMappings, event->key)) { + auto foundText = event->modifiers.testFlag(Qt::ShiftModifier) + ? findKeyTextByKeyId(DiacriticalCharsKeyToTextUppercase, event->key) + : findKeyTextByKeyId(DiacriticalCharsKeyToTextLowercase, event->key); + Q_ASSERT(foundText.has_value()); + event->text = foundText->size() == 1 ? *foundText : QString(); + } + + if (!event->text.isEmpty()) { if (event->type == EventType::KeyDown) { - Q_ASSERT(m_keyModifiedByDeadKeyOnPress == Qt::Key_unknown); - m_activeDeadKey = Qt::Key_unknown; + // Assume the first keypress with an active dead key is treated as modified, + // regardless of whether it has actually been modified or not. Take into account + // only events that produce actual key text. + if (!event->text.isEmpty()) + m_keyModifiedByDeadKeyOnPress = baseKey; } else { Q_ASSERT(event->type == EventType::KeyUp); Q_ASSERT(m_keyModifiedByDeadKeyOnPress == baseKey); - m_activeDeadKey = Qt::Key_unknown; m_keyModifiedByDeadKeyOnPress = Qt::Key_unknown; + m_activeDeadKey = Qt::Key_unknown; } - event->text = foundText->size() == 1 ? *foundText : QString(); - return; } } } diff --git a/tests/auto/wasm/CMakeLists.txt b/tests/auto/wasm/CMakeLists.txt index 0143d04142..47031037e0 100644 --- a/tests/auto/wasm/CMakeLists.txt +++ b/tests/auto/wasm/CMakeLists.txt @@ -29,3 +29,14 @@ qt_internal_add_test(tst_qwasmwindowstack Qt::Gui Qt::Widgets ) + +qt_internal_add_test(tst_qwasmkeytranslator + SOURCES + tst_qwasmkeytranslator.cpp + DEFINES + QT_NO_FOREACH + LIBRARIES + Qt::GuiPrivate + PUBLIC_LIBRARIES + Qt::Core +) diff --git a/tests/auto/wasm/tst_qwasmkeytranslator.cpp b/tests/auto/wasm/tst_qwasmkeytranslator.cpp new file mode 100644 index 0000000000..71a9164bbe --- /dev/null +++ b/tests/auto/wasm/tst_qwasmkeytranslator.cpp @@ -0,0 +1,425 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "../../../src/plugins/platforms/wasm/qwasmkeytranslator.h" + +#include "../../../src/plugins/platforms/wasm/qwasmevent.h" + +#include <QTest> + +#include <emscripten/val.h> + +namespace { +emscripten::val makeDeadKeyJsEvent(QString code, Qt::KeyboardModifiers modifiers) +{ + auto jsEvent = emscripten::val::object(); + jsEvent.set("code", emscripten::val(code.toStdString())); + jsEvent.set("key", emscripten::val("Dead")); + jsEvent.set("shiftKey", emscripten::val(modifiers.testFlag(Qt::ShiftModifier))); + jsEvent.set("ctrlKey", emscripten::val(false)); + jsEvent.set("altKey", emscripten::val(false)); + jsEvent.set("metaKey", emscripten::val(false)); + + return jsEvent; +} + +emscripten::val makeKeyJsEvent(QString code, QString key, Qt::KeyboardModifiers modifiers) +{ + auto jsEvent = emscripten::val::object(); + jsEvent.set("code", emscripten::val(code.toStdString())); + jsEvent.set("key", emscripten::val(key.toStdString())); + jsEvent.set("shiftKey", emscripten::val(modifiers.testFlag(Qt::ShiftModifier))); + jsEvent.set("ctrlKey", emscripten::val(modifiers.testFlag(Qt::ControlModifier))); + jsEvent.set("altKey", emscripten::val(modifiers.testFlag(Qt::AltModifier))); + jsEvent.set("metaKey", emscripten::val(modifiers.testFlag(Qt::MetaModifier))); + + return jsEvent; +} +} // unnamed namespace + +class tst_QWasmKeyTranslator : public QObject +{ + Q_OBJECT + +public: + tst_QWasmKeyTranslator() = default; + +private slots: + void init(); + + void modifyByDeadKey_data(); + void modifyByDeadKey(); + void deadKeyModifiesOnlyOneKeyPressAndUp(); + void deadKeyIgnoresKeyUpPrecedingKeyDown(); + void onlyKeysProducingTextAreModifiedByDeadKeys(); +}; + +void tst_QWasmKeyTranslator::init() { } + +void tst_QWasmKeyTranslator::modifyByDeadKey_data() +{ + QTest::addColumn<QString>("deadKeyCode"); + QTest::addColumn<Qt::KeyboardModifiers>("deadKeyModifiers"); + QTest::addColumn<QString>("targetKeyCode"); + QTest::addColumn<QString>("targetKey"); + QTest::addColumn<Qt::Key>("targetQtKey"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<QString>("expectedModifiedKey"); + + QTest::newRow("à (Backquote)") << "Backquote" << Qt::KeyboardModifiers() << "KeyA" + << "a" << Qt::Key_Agrave << Qt::KeyboardModifiers() << "à"; + QTest::newRow("À (Backquote)") + << "Backquote" << Qt::KeyboardModifiers() << "KeyA" + << "A" << Qt::Key_Agrave << Qt::KeyboardModifiers(Qt::ShiftModifier) << "À"; + QTest::newRow("à (IntlBackslash)") << "IntlBackslash" << Qt::KeyboardModifiers() << "KeyA" + << "a" << Qt::Key_Agrave << Qt::KeyboardModifiers() << "à"; + QTest::newRow("À (IntlBackslash)") + << "IntlBackslash" << Qt::KeyboardModifiers() << "KeyA" + << "A" << Qt::Key_Agrave << Qt::KeyboardModifiers(Qt::ShiftModifier) << "À"; + QTest::newRow("á (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyA" + << "a" << Qt::Key_Aacute << Qt::KeyboardModifiers() << "á"; + QTest::newRow("Á (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyA" + << "A" << Qt::Key_Aacute << Qt::KeyboardModifiers(Qt::ShiftModifier) + << "Á"; + QTest::newRow("á") << "KeyE" << Qt::KeyboardModifiers() << "KeyA" + << "a" << Qt::Key_Aacute << Qt::KeyboardModifiers() << "á"; + QTest::newRow("Á") << "KeyE" << Qt::KeyboardModifiers() << "KeyA" + << "A" << Qt::Key_Aacute << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Á"; + QTest::newRow("ä (Mac Umlaut)") << "KeyU" << Qt::KeyboardModifiers() << "KeyA" + << "a" << Qt::Key_Adiaeresis << Qt::KeyboardModifiers() << "ä"; + QTest::newRow("Ä (Mac Umlaut)") + << "KeyU" << Qt::KeyboardModifiers() << "KeyA" + << "A" << Qt::Key_Adiaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ä"; + QTest::newRow("ä (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyA" + << "a" << Qt::Key_Adiaeresis << Qt::KeyboardModifiers() << "ä"; + QTest::newRow("Ä (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyA" + << "A" << Qt::Key_Adiaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ä"; + QTest::newRow("â") << "KeyI" << Qt::KeyboardModifiers() << "KeyA" + << "a" << Qt::Key_Acircumflex << Qt::KeyboardModifiers() << "â"; + QTest::newRow("Â") << "KeyI" << Qt::KeyboardModifiers() << "KeyA" + << "A" << Qt::Key_Acircumflex << Qt::KeyboardModifiers(Qt::ShiftModifier) + << "Â"; + QTest::newRow("â (^ key)") << "Digit6" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyA" + << "a" << Qt::Key_Acircumflex << Qt::KeyboardModifiers() << "â"; + QTest::newRow("Â (^ key)") << "Digit6" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyA" + << "A" << Qt::Key_Acircumflex + << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Â"; + QTest::newRow("ã") << "KeyN" << Qt::KeyboardModifiers() << "KeyA" + << "a" << Qt::Key_Atilde << Qt::KeyboardModifiers() << "ã"; + QTest::newRow("Ã") << "KeyN" << Qt::KeyboardModifiers() << "KeyA" + << "A" << Qt::Key_Atilde << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ã"; + + QTest::newRow("è (Backquote)") << "Backquote" << Qt::KeyboardModifiers() << "KeyE" + << "e" << Qt::Key_Egrave << Qt::KeyboardModifiers() << "è"; + QTest::newRow("È (Backquote)") + << "Backquote" << Qt::KeyboardModifiers() << "KeyE" + << "E" << Qt::Key_Egrave << Qt::KeyboardModifiers(Qt::ShiftModifier) << "È"; + QTest::newRow("è") << "IntlBackslash" << Qt::KeyboardModifiers() << "KeyE" + << "e" << Qt::Key_Egrave << Qt::KeyboardModifiers() << "è"; + QTest::newRow("È") << "IntlBackslash" << Qt::KeyboardModifiers() << "KeyE" + << "E" << Qt::Key_Egrave << Qt::KeyboardModifiers(Qt::ShiftModifier) << "È"; + QTest::newRow("é") << "KeyE" << Qt::KeyboardModifiers() << "KeyE" + << "e" << Qt::Key_Eacute << Qt::KeyboardModifiers() << "é"; + QTest::newRow("É") << "KeyE" << Qt::KeyboardModifiers() << "KeyE" + << "E" << Qt::Key_Eacute << Qt::KeyboardModifiers(Qt::ShiftModifier) << "É"; + QTest::newRow("é (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyE" + << "e" << Qt::Key_Eacute << Qt::KeyboardModifiers() << "é"; + QTest::newRow("É (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyE" + << "E" << Qt::Key_Eacute << Qt::KeyboardModifiers(Qt::ShiftModifier) + << "É"; + QTest::newRow("ë (Mac Umlaut)") << "KeyU" << Qt::KeyboardModifiers() << "KeyE" + << "e" << Qt::Key_Ediaeresis << Qt::KeyboardModifiers() << "ë"; + QTest::newRow("Ë (Mac Umlaut)") + << "KeyU" << Qt::KeyboardModifiers() << "KeyE" + << "E" << Qt::Key_Ediaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ë"; + QTest::newRow("ë (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyE" + << "e" << Qt::Key_Ediaeresis << Qt::KeyboardModifiers() << "ë"; + QTest::newRow("Ë (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyE" + << "E" << Qt::Key_Ediaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ë"; + QTest::newRow("ê") << "KeyI" << Qt::KeyboardModifiers() << "KeyE" + << "e" << Qt::Key_Ecircumflex << Qt::KeyboardModifiers() << "ê"; + QTest::newRow("Ê") << "KeyI" << Qt::KeyboardModifiers() << "KeyE" + << "E" << Qt::Key_Ecircumflex << Qt::KeyboardModifiers(Qt::ShiftModifier) + << "Ê"; + QTest::newRow("ê (^ key)") << "Digit6" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyE" + << "e" << Qt::Key_Ecircumflex << Qt::KeyboardModifiers() << "ê"; + QTest::newRow("Ê (^ key)") << "Digit6" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyE" + << "E" << Qt::Key_Ecircumflex + << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ê"; + + QTest::newRow("ì (Backquote)") << "Backquote" << Qt::KeyboardModifiers() << "KeyI" + << "i" << Qt::Key_Igrave << Qt::KeyboardModifiers() << "ì"; + QTest::newRow("Ì (Backquote)") + << "Backquote" << Qt::KeyboardModifiers() << "KeyI" + << "I" << Qt::Key_Igrave << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ì"; + QTest::newRow("ì") << "IntlBackslash" << Qt::KeyboardModifiers() << "KeyI" + << "i" << Qt::Key_Igrave << Qt::KeyboardModifiers() << "ì"; + QTest::newRow("Ì") << "IntlBackslash" << Qt::KeyboardModifiers() << "KeyI" + << "I" << Qt::Key_Igrave << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ì"; + QTest::newRow("í") << "KeyE" << Qt::KeyboardModifiers() << "KeyI" + << "i" << Qt::Key_Iacute << Qt::KeyboardModifiers() << "í"; + QTest::newRow("Í") << "KeyE" << Qt::KeyboardModifiers() << "KeyI" + << "I" << Qt::Key_Iacute << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Í"; + QTest::newRow("í (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyI" + << "i" << Qt::Key_Iacute << Qt::KeyboardModifiers() << "í"; + QTest::newRow("Í (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyI" + << "I" << Qt::Key_Iacute << Qt::KeyboardModifiers(Qt::ShiftModifier) + << "Í"; + QTest::newRow("ï (Mac Umlaut)") << "KeyU" << Qt::KeyboardModifiers() << "KeyI" + << "i" << Qt::Key_Idiaeresis << Qt::KeyboardModifiers() << "ï"; + QTest::newRow("Ï (Mac Umlaut)") + << "KeyU" << Qt::KeyboardModifiers() << "KeyI" + << "I" << Qt::Key_Idiaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ï"; + QTest::newRow("ï (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyI" + << "i" << Qt::Key_Idiaeresis << Qt::KeyboardModifiers() << "ï"; + QTest::newRow("Ï (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyI" + << "I" << Qt::Key_Idiaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ï"; + QTest::newRow("î") << "KeyI" << Qt::KeyboardModifiers() << "KeyI" + << "i" << Qt::Key_Icircumflex << Qt::KeyboardModifiers() << "î"; + QTest::newRow("Î") << "KeyI" << Qt::KeyboardModifiers() << "KeyI" + << "I" << Qt::Key_Icircumflex << Qt::KeyboardModifiers(Qt::ShiftModifier) + << "Î"; + QTest::newRow("î (^ key)") << "Digit6" << Qt::KeyboardModifiers() << "KeyI" + << "i" << Qt::Key_Icircumflex << Qt::KeyboardModifiers() << "î"; + QTest::newRow("Î (^ key)") << "Digit6" << Qt::KeyboardModifiers() << "KeyI" + << "I" << Qt::Key_Icircumflex + << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Î"; + + QTest::newRow("ñ") << "KeyN" << Qt::KeyboardModifiers() << "KeyN" + << "n" << Qt::Key_Ntilde << Qt::KeyboardModifiers() << "ñ"; + QTest::newRow("Ñ") << "KeyN" << Qt::KeyboardModifiers() << "KeyN" + << "N" << Qt::Key_Ntilde << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ñ"; + + QTest::newRow("ò (Backquote)") << "Backquote" << Qt::KeyboardModifiers() << "KeyO" + << "o" << Qt::Key_Ograve << Qt::KeyboardModifiers() << "ò"; + QTest::newRow("Ò (Backquote)") + << "Backquote" << Qt::KeyboardModifiers() << "KeyO" + << "O" << Qt::Key_Ograve << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ò"; + QTest::newRow("ò") << "IntlBackslash" << Qt::KeyboardModifiers() << "KeyO" + << "o" << Qt::Key_Ograve << Qt::KeyboardModifiers() << "ò"; + QTest::newRow("Ò") << "IntlBackslash" << Qt::KeyboardModifiers() << "KeyO" + << "O" << Qt::Key_Ograve << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ò"; + QTest::newRow("ó") << "KeyE" << Qt::KeyboardModifiers() << "KeyO" + << "o" << Qt::Key_Oacute << Qt::KeyboardModifiers() << "ó"; + QTest::newRow("Ó") << "KeyE" << Qt::KeyboardModifiers() << "KeyO" + << "O" << Qt::Key_Oacute << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ó"; + QTest::newRow("ó (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyO" + << "o" << Qt::Key_Oacute << Qt::KeyboardModifiers() << "ó"; + QTest::newRow("Ó (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyO" + << "O" << Qt::Key_Oacute << Qt::KeyboardModifiers(Qt::ShiftModifier) + << "Ó"; + QTest::newRow("ö (Mac Umlaut)") << "KeyU" << Qt::KeyboardModifiers() << "KeyO" + << "o" << Qt::Key_Odiaeresis << Qt::KeyboardModifiers() << "ö"; + QTest::newRow("Ö (Mac Umlaut)") + << "KeyU" << Qt::KeyboardModifiers() << "KeyO" + << "O" << Qt::Key_Odiaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ö"; + QTest::newRow("ö (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyO" + << "o" << Qt::Key_Odiaeresis << Qt::KeyboardModifiers() << "ö"; + QTest::newRow("Ö (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyO" + << "O" << Qt::Key_Odiaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ö"; + QTest::newRow("ô") << "KeyI" << Qt::KeyboardModifiers() << "KeyO" + << "o" << Qt::Key_Ocircumflex << Qt::KeyboardModifiers() << "ô"; + QTest::newRow("Ô") << "KeyI" << Qt::KeyboardModifiers() << "KeyO" + << "O" << Qt::Key_Ocircumflex << Qt::KeyboardModifiers(Qt::ShiftModifier) + << "Ô"; + QTest::newRow("ô (^ key)") << "Digit6" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyO" + << "o" << Qt::Key_Ocircumflex << Qt::KeyboardModifiers() << "ô"; + QTest::newRow("Ô (^ key)") << "Digit6" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyO" + << "O" << Qt::Key_Ocircumflex + << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ô"; + QTest::newRow("õ") << "KeyN" << Qt::KeyboardModifiers() << "KeyO" + << "o" << Qt::Key_Otilde << Qt::KeyboardModifiers() << "õ"; + QTest::newRow("Õ") << "KeyN" << Qt::KeyboardModifiers() << "KeyO" + << "O" << Qt::Key_Otilde << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Õ"; + + QTest::newRow("ù (Backquote)") << "Backquote" << Qt::KeyboardModifiers() << "KeyU" + << "u" << Qt::Key_Ugrave << Qt::KeyboardModifiers() << "ù"; + QTest::newRow("Ù (Backquote)") + << "Backquote" << Qt::KeyboardModifiers() << "KeyU" + << "U" << Qt::Key_Ugrave << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ù"; + QTest::newRow("ù") << "IntlBackslash" << Qt::KeyboardModifiers() << "KeyU" + << "u" << Qt::Key_Ugrave << Qt::KeyboardModifiers() << "ù"; + QTest::newRow("Ù") << "IntlBackslash" << Qt::KeyboardModifiers() << "KeyU" + << "U" << Qt::Key_Ugrave << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ù"; + QTest::newRow("ú") << "KeyE" << Qt::KeyboardModifiers() << "KeyU" + << "u" << Qt::Key_Uacute << Qt::KeyboardModifiers() << "ú"; + QTest::newRow("Ú") << "KeyE" << Qt::KeyboardModifiers() << "KeyU" + << "U" << Qt::Key_Uacute << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ú"; + QTest::newRow("ú (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyU" + << "u" << Qt::Key_Uacute << Qt::KeyboardModifiers() << "ú"; + QTest::newRow("Ú (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyU" + << "U" << Qt::Key_Uacute << Qt::KeyboardModifiers(Qt::ShiftModifier) + << "Ú"; + QTest::newRow("ü (Mac Umlaut)") << "KeyU" << Qt::KeyboardModifiers() << "KeyU" + << "u" << Qt::Key_Udiaeresis << Qt::KeyboardModifiers() << "ü"; + QTest::newRow("Ü (Mac Umlaut)") + << "KeyU" << Qt::KeyboardModifiers() << "KeyU" + << "U" << Qt::Key_Udiaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ü"; + QTest::newRow("ü (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyU" + << "u" << Qt::Key_Udiaeresis << Qt::KeyboardModifiers() << "ü"; + QTest::newRow("Ü (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyU" + << "U" << Qt::Key_Udiaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ü"; + QTest::newRow("û") << "KeyI" << Qt::KeyboardModifiers() << "KeyU" + << "û" << Qt::Key_Ucircumflex << Qt::KeyboardModifiers() << "û"; + QTest::newRow("Û") << "KeyI" << Qt::KeyboardModifiers() << "KeyU" + << "U" << Qt::Key_Ucircumflex << Qt::KeyboardModifiers(Qt::ShiftModifier) + << "Û"; + QTest::newRow("û (^ key)") << "Digit6" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyU" + << "û" << Qt::Key_Ucircumflex << Qt::KeyboardModifiers() << "û"; + QTest::newRow("Û (^ key)") << "Digit6" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyU" + << "U" << Qt::Key_Ucircumflex + << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Û"; + + QTest::newRow("ý") << "KeyE" << Qt::KeyboardModifiers() << "KeyY" + << "y" << Qt::Key_Yacute << Qt::KeyboardModifiers() << "ý"; + QTest::newRow("Ý") << "KeyE" << Qt::KeyboardModifiers() << "KeyY" + << "Y" << Qt::Key_Yacute << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ý"; + QTest::newRow("ý (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyY" + << "y" << Qt::Key_Yacute << Qt::KeyboardModifiers() << "ý"; + QTest::newRow("Ý (Quote)") << "Quote" << Qt::KeyboardModifiers() << "KeyY" + << "Y" << Qt::Key_Yacute << Qt::KeyboardModifiers(Qt::ShiftModifier) + << "Ý"; + QTest::newRow("ÿ (Mac Umlaut)") << "KeyU" << Qt::KeyboardModifiers() << "KeyY" + << "y" << Qt::Key_ydiaeresis << Qt::KeyboardModifiers() << "ÿ"; + QTest::newRow("Ÿ (Mac Umlaut)") + << "KeyU" << Qt::KeyboardModifiers() << "KeyY" + << "Y" << Qt::Key_ydiaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ÿ"; + QTest::newRow("ÿ (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyY" + << "y" << Qt::Key_ydiaeresis << Qt::KeyboardModifiers() << "ÿ"; + QTest::newRow("Ÿ (Shift+Quote)") + << "Quote" << Qt::KeyboardModifiers(Qt::ShiftModifier) << "KeyY" + << "Y" << Qt::Key_ydiaeresis << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Ÿ"; +} + +void tst_QWasmKeyTranslator::modifyByDeadKey() +{ + QFETCH(QString, deadKeyCode); + QFETCH(Qt::KeyboardModifiers, deadKeyModifiers); + QFETCH(QString, targetKeyCode); + QFETCH(QString, targetKey); + QFETCH(Qt::Key, targetQtKey); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(QString, expectedModifiedKey); + + QWasmDeadKeySupport deadKeySupport; + + KeyEvent event(EventType::KeyDown, makeDeadKeyJsEvent(deadKeyCode, deadKeyModifiers)); + QCOMPARE(event.deadKey, true); + + deadKeySupport.applyDeadKeyTranslations(&event); + + KeyEvent eDown(EventType::KeyDown, makeKeyJsEvent(targetKeyCode, targetKey, modifiers)); + QCOMPARE(eDown.deadKey, false); + deadKeySupport.applyDeadKeyTranslations(&eDown); + QCOMPARE(eDown.deadKey, false); + QCOMPARE(eDown.text, expectedModifiedKey); + QCOMPARE(eDown.key, targetQtKey); + + KeyEvent eUp(EventType::KeyUp, makeKeyJsEvent(targetKeyCode, targetKey, modifiers)); + QCOMPARE(eUp.deadKey, false); + deadKeySupport.applyDeadKeyTranslations(&eUp); + QCOMPARE(eUp.text, expectedModifiedKey); + QCOMPARE(eUp.key, targetQtKey); +} + +void tst_QWasmKeyTranslator::deadKeyModifiesOnlyOneKeyPressAndUp() +{ + QWasmDeadKeySupport deadKeySupport; + KeyEvent event(EventType::KeyDown, makeDeadKeyJsEvent("KeyU", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&event); + + KeyEvent eDown(EventType::KeyDown, makeKeyJsEvent("KeyU", "u", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&eDown); + QCOMPARE(eDown.text, "ü"); + QCOMPARE(eDown.key, Qt::Key_Udiaeresis); + + KeyEvent eUp(EventType::KeyUp, makeKeyJsEvent("KeyU", "u", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&eUp); + QCOMPARE(eUp.text, "ü"); + QCOMPARE(eUp.key, Qt::Key_Udiaeresis); + + KeyEvent eDown2(EventType::KeyDown, makeKeyJsEvent("KeyU", "u", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&eDown2); + QCOMPARE(eDown2.text, "u"); + QCOMPARE(eDown2.key, Qt::Key_U); + + KeyEvent eUp2(EventType::KeyUp, makeKeyJsEvent("KeyU", "u", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&eUp2); + QCOMPARE(eUp2.text, "u"); + QCOMPARE(eUp2.key, Qt::Key_U); +} + +void tst_QWasmKeyTranslator::deadKeyIgnoresKeyUpPrecedingKeyDown() +{ + QWasmDeadKeySupport deadKeySupport; + + KeyEvent deadKeyDownEvent(EventType::KeyDown, + makeDeadKeyJsEvent("KeyU", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&deadKeyDownEvent); + + KeyEvent deadKeyUpEvent(EventType::KeyUp, makeDeadKeyJsEvent("KeyU", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&deadKeyUpEvent); + + KeyEvent otherKeyUpEvent(EventType::KeyUp, + makeKeyJsEvent("AltLeft", "Alt", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&otherKeyUpEvent); + + KeyEvent eDown(EventType::KeyDown, makeKeyJsEvent("KeyU", "u", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&eDown); + QCOMPARE(eDown.text, "ü"); + QCOMPARE(eDown.key, Qt::Key_Udiaeresis); + + KeyEvent yetAnotherKeyUpEvent( + EventType::KeyUp, makeKeyJsEvent("ControlLeft", "Control", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&yetAnotherKeyUpEvent); + + KeyEvent eUp(EventType::KeyUp, makeKeyJsEvent("KeyU", "u", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&eUp); + QCOMPARE(eUp.text, "ü"); + QCOMPARE(eUp.key, Qt::Key_Udiaeresis); +} + +void tst_QWasmKeyTranslator::onlyKeysProducingTextAreModifiedByDeadKeys() +{ + QWasmDeadKeySupport deadKeySupport; + + KeyEvent deadKeyDownEvent(EventType::KeyDown, + makeDeadKeyJsEvent("KeyU", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&deadKeyDownEvent); + + KeyEvent noTextKeyDown(EventType::KeyDown, + makeKeyJsEvent("AltLeft", "Alt", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&noTextKeyDown); + QCOMPARE(noTextKeyDown.text, ""); + QCOMPARE(noTextKeyDown.key, Qt::Key_Alt); + + KeyEvent noTextKeyUp(EventType::KeyUp, + makeKeyJsEvent("AltLeft", "Alt", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&noTextKeyUp); + QCOMPARE(noTextKeyDown.text, ""); + QCOMPARE(noTextKeyDown.key, Qt::Key_Alt); + + KeyEvent eDown(EventType::KeyDown, makeKeyJsEvent("KeyU", "u", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&eDown); + QCOMPARE(eDown.text, "ü"); + QCOMPARE(eDown.key, Qt::Key_Udiaeresis); + + KeyEvent eUp(EventType::KeyUp, makeKeyJsEvent("KeyU", "u", Qt::KeyboardModifiers())); + deadKeySupport.applyDeadKeyTranslations(&eUp); + QCOMPARE(eUp.text, "ü"); + QCOMPARE(eUp.key, Qt::Key_Udiaeresis); +} + +QTEST_MAIN(tst_QWasmKeyTranslator) +#include "tst_qwasmkeytranslator.moc" |