Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 720: Spanish Keyboard Layout Accents #721

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions src/gui/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <QMap>
#include <QVariant>

#include <QDebug>

namespace NeovimQt { namespace Input {

const QMap<int, QString>& GetSpecialKeysMap() noexcept
Expand Down Expand Up @@ -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() };
Expand Down Expand Up @@ -191,6 +213,11 @@ QString convertKey(const QKeyEvent& ev) noexcept
const QMap<int, QString>& 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 <S-Space>.
// Issue#259: Shift + BackSpace inserts 7;2u in `:terminal`. Incorrectly sent as <S-BS>.
if (key == Qt::Key_Space
Expand All @@ -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 <C-^>
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");
}

Expand Down
8 changes: 5 additions & 3 deletions src/gui/input_mac.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "input.h"
#include <QDebug>

namespace NeovimQt { namespace Input {

Expand Down Expand Up @@ -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;
}

Expand Down
4 changes: 2 additions & 2 deletions src/gui/shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 5 additions & 4 deletions test/tst_input.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#pragma once

#include <QKeyEvent>
#include <QString>

// 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;
};
49 changes: 43 additions & 6 deletions test/tst_input_mac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ private slots:
void CtrlCaretWellFormed() noexcept;
void ShiftModifierLetter() noexcept;
void GermanKeyboardLayout() noexcept;
void SpanishKeyboardLayout() noexcept;
};

void TestInputMac::AltSpecialCharacters() noexcept
Expand Down Expand Up @@ -47,17 +48,22 @@ void TestInputMac::SpecialKeys() noexcept
const QList<int> 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<InputTest> keyEventList{
{ QEvent::KeyPress, k, Qt::NoModifier, "<%1>" },
{ QEvent::KeyPress, k, Qt::ControlModifier, "<D-%1>" },
{ QEvent::KeyPress, k, Qt::AltModifier, "<A-%1>" },
{ QEvent::KeyPress, k, Qt::MetaModifier, "<C-%1>" },
{ { QEvent::KeyPress, k, Qt::NoModifier, text }, "<%1>" },
{ { QEvent::KeyPress, k, Qt::ControlModifier, text }, "<D-%1>" },
{ { QEvent::KeyPress, k, Qt::AltModifier, text }, "<A-%1>" },
{ { QEvent::KeyPress, k, Qt::MetaModifier,text }, "<C-%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)));
}
}
Expand Down Expand Up @@ -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)
25 changes: 19 additions & 6 deletions test/tst_input_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ private slots:
void ShiftModifierLetter() noexcept;
void GermanKeyboardLayout() noexcept;
void ControlSpace() noexcept;
void SpanishKeyboardLayout() noexcept;
};

void TestInputUnix::LessThanModifierKeys() noexcept
Expand All @@ -33,17 +34,22 @@ void TestInputUnix::SpecialKeys() noexcept
const QList<int> 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<InputTest> keyEventList{
{ QEvent::KeyPress, k, Qt::NoModifier, "<%1>" },
{ QEvent::KeyPress, k, Qt::ControlModifier, "<C-%1>" },
{ QEvent::KeyPress, k, Qt::AltModifier, "<A-%1>" },
{ QEvent::KeyPress, k, Qt::MetaModifier, "<D-%1>" },
{ { QEvent::KeyPress, k, Qt::NoModifier, text }, "<%1>" },
{ { QEvent::KeyPress, k, Qt::ControlModifier, text }, "<C-%1>" },
{ { QEvent::KeyPress, k, Qt::AltModifier, text }, "<A-%1>" },
{ { QEvent::KeyPress, k, Qt::MetaModifier, text }, "<D-%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)));
}
}
Expand Down Expand Up @@ -105,5 +111,12 @@ void TestInputUnix::ControlSpace() noexcept
QCOMPARE(NeovimQt::Input::convertKey(evControlSpace), QString{ "<C-Space>" });
}

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)
56 changes: 50 additions & 6 deletions test/tst_input_win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ private slots:
void ShiftModifierLetter() noexcept;
void GermanKeyboardLayout() noexcept;
void FrenchBepoKeyboardLayout() noexcept;
void SpanishKeyboardLayout() noexcept;
};

void TestInputWin32::LessThanModifierKeys() noexcept
Expand All @@ -33,17 +34,22 @@ void TestInputWin32::SpecialKeys() noexcept
const QList<int> 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<InputTest> keyEventList{
{ QEvent::KeyPress, k, Qt::NoModifier, "<%1>" },
{ QEvent::KeyPress, k, Qt::ControlModifier, "<C-%1>" },
{ QEvent::KeyPress, k, Qt::AltModifier, "<A-%1>" },
{ QEvent::KeyPress, k, Qt::MetaModifier, "<%1>" },
{ { QEvent::KeyPress, k, Qt::NoModifier, text }, "<%1>" },
{ { QEvent::KeyPress, k, Qt::ControlModifier, text }, "<C-%1>" },
{ { QEvent::KeyPress, k, Qt::AltModifier, text }, "<A-%1>" },
{ { 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)));
}
}
Expand Down Expand Up @@ -112,5 +118,43 @@ void TestInputWin32::FrenchBepoKeyboardLayout() noexcept
QCOMPARE(NeovimQt::Input::convertKey(evSpace), QString{ "<Space>" });
}

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)