From 7487deef634e51f13bd13afc078a4e2cba477065 Mon Sep 17 00:00:00 2001 From: John Gehrig Date: Thu, 9 Jul 2020 13:26:27 -0400 Subject: [PATCH] Issue 720: Spanish Keyboard Layout Accents Accent characters are not handled correctly for the Spanish Keyboard Layout. TODO Adding tests only, not sure if these are correct... TODO Fix For Windows. Validate MacOS/Linux --- src/gui/input.cpp | 39 ++++++++++++++++++++++++++-- test/tst_input.h | 9 ++++--- test/tst_input_common.cpp | 54 +++++++++++++++++++++++++++++++++++++++ test/tst_input_mac.cpp | 17 +++++++----- test/tst_input_unix.cpp | 17 +++++++----- test/tst_input_win32.cpp | 17 +++++++----- 6 files changed, 129 insertions(+), 24 deletions(-) diff --git a/src/gui/input.cpp b/src/gui/input.cpp index a0d8b21e4b..caec0a4736 100644 --- a/src/gui/input.cpp +++ b/src/gui/input.cpp @@ -141,6 +141,26 @@ static QString KeyToText(int key, Qt::KeyboardModifiers mod) noexcept return text; } +static bool IsControlCaretKeyEvent( + int key, + Qt::KeyboardModifiers mod, + const QString& text) noexcept +{ + if (key != Qt::Key_6 && key != Qt::Key_AsciiCircum) { + return false; + } + + if (!(mod & ControlModifier())) { + return false; + } + + if (text != "\u001E" && text != "^" && text != "6" && !text.isEmpty()) { + return false; + } + + return true; +} + QString convertKey(const QKeyEvent& ev) noexcept { QString text{ ev.text() }; @@ -177,6 +197,11 @@ QString convertKey(const QKeyEvent& ev) noexcept const QMap& specialKeys { GetSpecialKeysMap() }; if (specialKeys.contains(key)) { + // Issue#720: International keyboards may insert an accent on space. + if (key == Qt::Key_Space && text != " ") { + return text; + } + // Issue#728: Shift + Space inserts ;2u in `:terminal`. Incorrectly sent as . // Issue#259: Shift + BackSpace inserts 7;2u in `:terminal`. Incorrectly sent as . if (key == Qt::Key_Space @@ -194,9 +219,19 @@ QString convertKey(const QKeyEvent& ev) noexcept return ToKeyString(GetModifierPrefix(modNoShift), "lt"); } + // Issue#720: Spanish keyboard "[" character insertion + if (key == Qt::Key_AsciiCircum && text == "[") { + const Qt::KeyboardModifiers modNoAlt{ mod & ~Qt::AltModifier }; + + if (modNoAlt == Qt::NoModifier) { + return QStringLiteral("["); + } + + return ToKeyString(GetModifierPrefix(modNoAlt), "["); + } + // Issue#170: Normalize modifiers, CTRL+^ always sends as - const bool isCaretKey{ key == Qt::Key_6 || key == Qt::Key_AsciiCircum }; - if (isCaretKey && mod & ControlModifier()) { + if (IsControlCaretKeyEvent(key, mod, text)) { const Qt::KeyboardModifiers modNoShiftMeta{ mod & ~Qt::KeyboardModifier::ShiftModifier & ~CmdModifier() }; return ToKeyString(GetModifierPrefix(modNoShiftMeta), "^"); diff --git a/test/tst_input.h b/test/tst_input.h index 59913044bf..d72f397c0d 100644 --- a/test/tst_input.h +++ b/test/tst_input.h @@ -1,11 +1,12 @@ #pragma once +#include +#include + // A class to hold test data. An event type/key/modifiers // as used in QKeyEvent and a matching Neovim input string. -struct InputTest +struct InputTest final { - QEvent::Type event_type; - int key; - Qt::KeyboardModifiers modifiers; + QKeyEvent event; QString expected_input; }; diff --git a/test/tst_input_common.cpp b/test/tst_input_common.cpp index a5fc613b29..a3de8d93f1 100644 --- a/test/tst_input_common.cpp +++ b/test/tst_input_common.cpp @@ -17,6 +17,7 @@ private slots: void AltGrKeyEventWellFormed() noexcept; void ShiftSpaceWellFormed() noexcept; void ShiftBackSpaceWellFormed() noexcept; + void SpanishKeyboardLayout() noexcept; // Mouse Input void MouseLeftClick() noexcept; @@ -265,5 +266,58 @@ void TestInputCommon::MouseMiddleClick() noexcept QCOMPARE(middleClickRelease, QString{ "<1,2>" }); } +void TestInputCommon::SpanishKeyboardLayout() noexcept +{ + // Issue 720: Spanish layout ignores Left Square Bracket [ + // NOTE: The "`" referenced below is "[" on a US layout keyboard for Windows/Linux and literal for MacOS. + + // Windows ` + Space. Prints: ` + QKeyEvent evAccentSpace{ QKeyEvent::KeyPress, Qt::Key_Space, Qt::NoModifier, QStringLiteral("`") }; + QCOMPARE(NeovimQt::Input::convertKey(evAccentSpace), QStringLiteral("`")); + + // Windows ``: two events are sent on the second key event. Prints: `` + // NOTE: Linux/MacOS do not send QKeyEvents for this scenario. + QKeyEvent evAccentFirst{ QKeyEvent::KeyPress, Qt::Key_QuoteLeft, Qt::NoModifier, QStringLiteral("`") }; + QKeyEvent evAccentSecond{ QKeyEvent::KeyPress, 0, Qt::NoModifier, QStringLiteral("`") }; + + // Windows AltGr (Right Alt) + `. Prints: [ + QKeyEvent evAltGrSquareBracketWindows{ QKeyEvent::KeyPress, Qt::Key_AsciiCircum, Qt::AltModifier, QStringLiteral("[") }; + QCOMPARE(NeovimQt::Input::convertKey(evAltGrSquareBracketWindows), QStringLiteral("[")); + + // Linux AltGr (Right Alt) + `. Prints: [ + QKeyEvent evAltGrSquareBracketLinux{ QKeyEvent::KeyPress, Qt::Key_BracketLeft, Qt::GroupSwitchModifier, QStringLiteral("[") }; + QCOMPARE(NeovimQt::Input::convertKey(evAltGrSquareBracketLinux), QStringLiteral("[")); + +// // MacOS Alt + `: Prints [ +// QKeyEvent evAltLeftSquareBracketMacOS{ QKeyEvent::KeyPress, Qt::Key_Less, Qt::AltModifier, QStringLiteral("[") }; +// QCOMPARE(NeovimQt::Input::convertKey(evAltLeftSquareBracketMacOS), QStringLiteral("[")); +// +// // MacOS Alt + \: Prints [ +// QKeyEvent evAltRightSquareBracketMacOS{ QKeyEvent::KeyPress, Qt::Key_Apostrophe, Qt::AltModifier, QStringLiteral("]") }; +// QCOMPARE(NeovimQt::Input::convertKey(evAltRightSquareBracketMacOS), QStringLiteral("[")); + + // Windows Shift + ` then Space. Prints ^ + // NOTE: Linux does not send QKeyEvents for this scenario. + QKeyEvent evShiftAccentSpace{ QKeyEvent::KeyPress, Qt::Key_Space, Qt::NoModifier, QStringLiteral("^") }; + QCOMPARE(NeovimQt::Input::convertKey(evShiftAccentSpace), QStringLiteral("^")); + + // Windows Shift + ``. Prints ^^ (Windows) and ^ (Linux) + // NOTE: Linux/MacOS do not send QKeyEvents for this scenario. + QKeyEvent evShiftAccentAccent1{ QKeyEvent::KeyPress, Qt::Key_AsciiCircum, Qt::ShiftModifier, QStringLiteral("^") }; + QKeyEvent evShiftAccentAccent2{ QKeyEvent::KeyPress, 0, Qt::ShiftModifier, QStringLiteral("^") }; + QCOMPARE(NeovimQt::Input::convertKey(evShiftAccentAccent1), QStringLiteral("^")); + QCOMPARE(NeovimQt::Input::convertKey(evShiftAccentAccent2), QStringLiteral("^")); + + // Windows ` then e. Prints: è + // NOTE: Linux/MacOS do not send QKeyEvents for this scenario. + QKeyEvent evAccentE{ QKeyEvent::KeyPress, Qt::Key_E, Qt::NoModifier, QStringLiteral("ê") }; + QCOMPARE(NeovimQt::Input::convertKey(evAccentE), QStringLiteral("ê")); + + // Windows Shift + ^ then e. Prints: ê + // NOTE: Linux/MacOS do not send QKeyEvents for this scenario. + QKeyEvent evShiftAccentE{ QKeyEvent::KeyPress, Qt::Key_E, Qt::NoModifier, QStringLiteral("ê") }; + QCOMPARE(NeovimQt::Input::convertKey(evShiftAccentE), QStringLiteral("ê")); +} + #include "tst_input_common.moc" QTEST_MAIN(TestInputCommon) diff --git a/test/tst_input_mac.cpp b/test/tst_input_mac.cpp index 6b2e8a9d25..3237e88bec 100644 --- a/test/tst_input_mac.cpp +++ b/test/tst_input_mac.cpp @@ -47,17 +47,22 @@ void TestInputMac::SpecialKeys() noexcept const QList specialKeys{ NeovimQt::Input::GetSpecialKeysMap().keys() }; for (const auto k : specialKeys) { + // Key_Space events send with text=" " + QString text; + if (k == Qt::Key_Space) { + text = QStringLiteral(" "); + } + // On Mac Meta is the Control key, treated as C-. QList keyEventList{ - { QEvent::KeyPress, k, Qt::NoModifier, "<%1>" }, - { QEvent::KeyPress, k, Qt::ControlModifier, "" }, - { QEvent::KeyPress, k, Qt::AltModifier, "" }, - { QEvent::KeyPress, k, Qt::MetaModifier, "" }, + { { QEvent::KeyPress, k, Qt::NoModifier, text }, "<%1>" }, + { { QEvent::KeyPress, k, Qt::ControlModifier, text }, "" }, + { { QEvent::KeyPress, k, Qt::AltModifier, text }, "" }, + { { QEvent::KeyPress, k, Qt::MetaModifier,text }, "" }, }; for (const auto& keyTest : keyEventList) { - auto event = QKeyEvent(keyTest.event_type, keyTest.key, keyTest.modifiers); - QCOMPARE(NeovimQt::Input::convertKey(event), + QCOMPARE(NeovimQt::Input::convertKey(keyTest.event), keyTest.expected_input.arg(NeovimQt::Input::GetSpecialKeysMap().value(k))); } } diff --git a/test/tst_input_unix.cpp b/test/tst_input_unix.cpp index f65e3959ee..f820d40c80 100644 --- a/test/tst_input_unix.cpp +++ b/test/tst_input_unix.cpp @@ -32,17 +32,22 @@ void TestInputUnix::SpecialKeys() noexcept const QList specialKeys{ NeovimQt::Input::GetSpecialKeysMap().keys() }; for (const auto k : specialKeys) { + // Key_Space events send with text=" " + QString text; + if (k == Qt::Key_Space) { + text = QStringLiteral(" "); + } + // On Mac Meta is the Control key, treated as C-. QList keyEventList{ - { QEvent::KeyPress, k, Qt::NoModifier, "<%1>" }, - { QEvent::KeyPress, k, Qt::ControlModifier, "" }, - { QEvent::KeyPress, k, Qt::AltModifier, "" }, - { QEvent::KeyPress, k, Qt::MetaModifier, "" }, + { { QEvent::KeyPress, k, Qt::NoModifier, text }, "<%1>" }, + { { QEvent::KeyPress, k, Qt::ControlModifier, text }, "" }, + { { QEvent::KeyPress, k, Qt::AltModifier, text }, "" }, + { { QEvent::KeyPress, k, Qt::MetaModifier, text }, "" }, }; for (const auto& keyTest : keyEventList) { - auto event = QKeyEvent(keyTest.event_type, keyTest.key, keyTest.modifiers); - QCOMPARE(NeovimQt::Input::convertKey(event), + QCOMPARE(NeovimQt::Input::convertKey(keyTest.event), keyTest.expected_input.arg(NeovimQt::Input::GetSpecialKeysMap().value(k))); } } diff --git a/test/tst_input_win32.cpp b/test/tst_input_win32.cpp index 6a1f798bf8..876e02d899 100644 --- a/test/tst_input_win32.cpp +++ b/test/tst_input_win32.cpp @@ -32,17 +32,22 @@ void TestInputWin32::SpecialKeys() noexcept const QList specialKeys{ NeovimQt::Input::GetSpecialKeysMap().keys() }; for (const auto k : specialKeys) { + // Key_Space events send with text=" " + QString text; + if (k == Qt::Key_Space) { + text = QStringLiteral(" "); + } + // On Mac Meta is the Control key, treated as C-. QList keyEventList{ - { QEvent::KeyPress, k, Qt::NoModifier, "<%1>" }, - { QEvent::KeyPress, k, Qt::ControlModifier, "" }, - { QEvent::KeyPress, k, Qt::AltModifier, "" }, - { QEvent::KeyPress, k, Qt::MetaModifier, "<%1>" }, + { { QEvent::KeyPress, k, Qt::NoModifier, text }, "<%1>" }, + { { QEvent::KeyPress, k, Qt::ControlModifier, text }, "" }, + { { QEvent::KeyPress, k, Qt::AltModifier, text }, "" }, + { { QEvent::KeyPress, k, Qt::MetaModifier, text }, "<%1>" }, }; for (const auto& keyTest : keyEventList) { - auto event = QKeyEvent(keyTest.event_type, keyTest.key, keyTest.modifiers); - QCOMPARE(NeovimQt::Input::convertKey(event), + QCOMPARE(NeovimQt::Input::convertKey(keyTest.event), keyTest.expected_input.arg(NeovimQt::Input::GetSpecialKeysMap().value(k))); } }