diff --git a/src/gui/input.cpp b/src/gui/input.cpp index e6bf88a06..cf4b310c3 100644 --- a/src/gui/input.cpp +++ b/src/gui/input.cpp @@ -3,6 +3,8 @@ #include #include +#include + namespace NeovimQt { namespace Input { const QMap& GetSpecialKeysMap() noexcept @@ -141,6 +143,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() }; @@ -191,6 +213,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 @@ -208,15 +235,26 @@ 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), "^"); } if (text == "\\") { + qDebug() << "Bail EARLY BACKSLASH!"; return ToKeyString(GetModifierPrefix(mod), "Bslash"); } diff --git a/src/gui/input_mac.cpp b/src/gui/input_mac.cpp index b0588593a..61a457a16 100644 --- a/src/gui/input_mac.cpp +++ b/src/gui/input_mac.cpp @@ -1,4 +1,5 @@ #include "input.h" +#include namespace NeovimQt { namespace Input { @@ -56,13 +57,14 @@ static bool IsAsciiCharRequiringAlt(int key, Qt::KeyboardModifiers mod, QChar c) } // These low-ascii characters may require AltModifier on MacOS - if ((c == '[' && key != Qt::Key_BracketLeft) + if ((c == '[' /*&& key != Qt::Key_BracketLeft*/) // FIXME || (c == ']' && key != Qt::Key_BracketRight) - || (c == '{' && key != Qt::Key_BraceLeft) + || (c == '{' /*&& key != Qt::Key_BraceLeft*/) // FIXME || (c == '}' && key != Qt::Key_BraceRight) || (c == '|' && key != Qt::Key_Bar) || (c == '~' && key != Qt::Key_AsciiTilde) - || (c == '@' && key != Qt::Key_At)) { + || (c == '@' && key != Qt::Key_At) + || (c == '#' && key != Qt::Key_NumberSign)) { return true; } diff --git a/src/gui/shell.cpp b/src/gui/shell.cpp index ee67cda50..357bdaea1 100644 --- a/src/gui/shell.cpp +++ b/src/gui/shell.cpp @@ -1365,8 +1365,8 @@ void Shell::keyPressEvent(QKeyEvent *ev) const QString inp{ Input::convertKey(*ev) }; // Uncomment for key input debugging and unit test writing. - // qDebug() << "QKeyEvent ev:" << ev; - // qDebug() << " " << inp; + qDebug() << "QKeyEvent ev:" << ev; + qDebug() << " " << inp; if (inp.isEmpty()) { QWidget::keyPressEvent(ev); diff --git a/test/tst_input.h b/test/tst_input.h index 59913044b..d72f397c0 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_mac.cpp b/test/tst_input_mac.cpp index 6b2e8a9d2..fa4ec3370 100644 --- a/test/tst_input_mac.cpp +++ b/test/tst_input_mac.cpp @@ -15,6 +15,7 @@ private slots: void CtrlCaretWellFormed() noexcept; void ShiftModifierLetter() noexcept; void GermanKeyboardLayout() noexcept; + void SpanishKeyboardLayout() noexcept; }; void TestInputMac::AltSpecialCharacters() noexcept @@ -47,17 +48,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))); } } @@ -131,5 +137,36 @@ void TestInputMac::GermanKeyboardLayout() noexcept QCOMPARE(NeovimQt::Input::convertKey(evOptionAtSign), QString{ "@" }); } +// FIXME Issue 720: Spanish layout ignores Left Square Bracket [ +void TestInputMac::SpanishKeyboardLayout() noexcept +{ + QKeyEvent evBracketRight{ QKeyEvent::KeyPress, Qt::Key_Plus, Qt::AltModifier, QStringLiteral("]") }; + QCOMPARE(NeovimQt::Input::convertKey(evBracketRight), QStringLiteral("]")); + + QKeyEvent evBracketLeft{ QKeyEvent::KeyPress, Qt::Key_BracketLeft, Qt::AltModifier, QStringLiteral("[") }; + QCOMPARE(NeovimQt::Input::convertKey(evBracketLeft), QStringLiteral("[")); + + QKeyEvent evBraceRight{ QKeyEvent::KeyPress, Qt::Key_Ccedilla, Qt::AltModifier, QStringLiteral("}") }; + QCOMPARE(NeovimQt::Input::convertKey(evBraceRight), QStringLiteral("}")); + + QKeyEvent evBraceLeft{ QKeyEvent::KeyPress, Qt::Key_BraceLeft, Qt::AltModifier, QStringLiteral("{") }; + QCOMPARE(NeovimQt::Input::convertKey(evBraceLeft), QStringLiteral("{")); + + QKeyEvent evPipe{ QKeyEvent::KeyPress, Qt::Key_1, Qt::AltModifier, QStringLiteral("|") }; + QCOMPARE(NeovimQt::Input::convertKey(evPipe), QStringLiteral("|")); + + QKeyEvent evAt{ QKeyEvent::KeyPress, Qt::Key_2, Qt::AltModifier, QStringLiteral("@") }; + QCOMPARE(NeovimQt::Input::convertKey(evAt), QStringLiteral("@")); + + QKeyEvent evNegation{ QKeyEvent::KeyPress, Qt::Key_6, Qt::AltModifier, QStringLiteral("¬") }; + QCOMPARE(NeovimQt::Input::convertKey(evNegation), QStringLiteral("¬")); + + QKeyEvent evPound{ QKeyEvent::KeyPress, Qt::Key_3, Qt::AltModifier, QStringLiteral("#") }; + QCOMPARE(NeovimQt::Input::convertKey(evPound), QStringLiteral("#")); + +// QKeyEvent evBackslash{ QKeyEvent::KeyPress, Qt::Key_masculine, Qt::AltModifier, QStringLiteral("\\") }; +// QCOMPARE(NeovimQt::Input::convertKey(evBackslash), QStringLiteral("\\")); +} + #include "tst_input_mac.moc" QTEST_MAIN(TestInputMac) diff --git a/test/tst_input_unix.cpp b/test/tst_input_unix.cpp index 80f7f0390..05f15fcf5 100644 --- a/test/tst_input_unix.cpp +++ b/test/tst_input_unix.cpp @@ -14,6 +14,7 @@ private slots: void ShiftModifierLetter() noexcept; void GermanKeyboardLayout() noexcept; void ControlSpace() noexcept; + void SpanishKeyboardLayout() noexcept; }; void TestInputUnix::LessThanModifierKeys() noexcept @@ -33,17 +34,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))); } } @@ -105,5 +111,12 @@ void TestInputUnix::ControlSpace() noexcept QCOMPARE(NeovimQt::Input::convertKey(evControlSpace), QString{ "" }); } +void TestInputUnix::SpanishKeyboardLayout() noexcept +{ + // Linux AltGr (Right Alt) + `. Prints: [ + QKeyEvent evAltGrSquareBracketLinux{ QKeyEvent::KeyPress, Qt::Key_BracketLeft, Qt::GroupSwitchModifier, QStringLiteral("[") }; + QCOMPARE(NeovimQt::Input::convertKey(evAltGrSquareBracketLinux), QStringLiteral("[")); +} + #include "tst_input_unix.moc" QTEST_MAIN(TestInputUnix) diff --git a/test/tst_input_win32.cpp b/test/tst_input_win32.cpp index f4fe1d78f..2d711194d 100644 --- a/test/tst_input_win32.cpp +++ b/test/tst_input_win32.cpp @@ -14,6 +14,7 @@ private slots: void ShiftModifierLetter() noexcept; void GermanKeyboardLayout() noexcept; void FrenchBepoKeyboardLayout() noexcept; + void SpanishKeyboardLayout() noexcept; }; void TestInputWin32::LessThanModifierKeys() noexcept @@ -33,17 +34,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))); } } @@ -112,5 +118,43 @@ void TestInputWin32::FrenchBepoKeyboardLayout() noexcept QCOMPARE(NeovimQt::Input::convertKey(evSpace), QString{ "" }); } +void TestInputWin32::SpanishKeyboardLayout() noexcept +{ + // 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("[")); + + // 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_win32.moc" QTEST_MAIN(TestInputWin32)