- Full (iPad Pro) mode: every typing key from the keymap, number row, tab, caps lock, ctrl/super/alt; shifted symbol shown above and flickable - Dead keys compose with the locale's XKB compose table - Ctrl/Alt/Super stick like shift and turn keys into shortcuts; a tapped or held shift joins shortcuts and non-text keys, auto-shift and caps lock don't - Auto-capitalization and caps lock only capitalize letters; the user's shift picks the keymap's shifted level - Mode switch in settings keeps key size by resizing the window Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
133 lines
4.5 KiB
C++
133 lines
4.5 KiB
C++
#include "TestLayouts.h"
|
|
#include "layout/KeyGeometry.h"
|
|
#include "layout/LayoutLoader.h"
|
|
|
|
#include <QTest>
|
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
class TestLayoutLoader : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private Q_SLOTS:
|
|
void parsesStringAndObjectKeys()
|
|
{
|
|
QString error;
|
|
const auto layout = LayoutLoader::parse(R"({
|
|
"id": "t", "layers": [{"name": "letters", "rows": [[
|
|
"q",
|
|
{"label": "e", "flick": "3", "alternates": ["é", "è"]},
|
|
{"type": "backspace", "width": 1.5}
|
|
]]}]
|
|
})", &error);
|
|
QVERIFY2(layout, qPrintable(error));
|
|
|
|
const Row &row = layout->layers.first().rows.first();
|
|
QCOMPARE(row.size(), 3);
|
|
QCOMPARE(row[0].label, u"q");
|
|
QCOMPARE(row[1].flick, u"3");
|
|
QCOMPARE(row[1].alternates, QStringList({u"é"_s, u"è"_s}));
|
|
QCOMPARE(row[2].type, KeyType::Backspace);
|
|
QCOMPARE(row[2].width, 1.5);
|
|
}
|
|
|
|
void rejectsUnknownKeyType()
|
|
{
|
|
QString error;
|
|
QVERIFY(!LayoutLoader::parse(R"({"id": "t", "layers": [{"name": "l", "rows": [[{"type": "warp"}]]}]})", &error));
|
|
QVERIFY(error.contains(u"warp"));
|
|
}
|
|
|
|
void rejectsMissingId()
|
|
{
|
|
QString error;
|
|
QVERIFY(!LayoutLoader::parse(R"({"layers": []})", &error));
|
|
}
|
|
|
|
void validateCatchesUnknownLayerTarget()
|
|
{
|
|
QString error;
|
|
const auto layout = LayoutLoader::parse(
|
|
R"({"id": "t", "layers": [{"name": "l", "rows": [[{"type": "layer", "target": "nope"}]]}]})", &error);
|
|
QVERIFY(layout);
|
|
QVERIFY(!LayoutLoader::validate(*layout, &error));
|
|
}
|
|
|
|
void templateIsValid()
|
|
{
|
|
QString error;
|
|
QVERIFY2(LayoutLoader::validate(TestLayouts::builtinTemplate(), &error), qPrintable(error));
|
|
}
|
|
|
|
void rejectsLettersWithoutRow()
|
|
{
|
|
QString error;
|
|
QVERIFY(!LayoutLoader::parse(R"({"id": "t", "layers": [{"name": "l", "rows": [[{"type": "letters"}]]}]})",
|
|
&error));
|
|
}
|
|
|
|
// Stretch keys must even out rows whatever the keymap's letter counts.
|
|
void builtLayersHaveEvenRows_data()
|
|
{
|
|
QTest::addColumn<QString>("xkbLayout");
|
|
QTest::addColumn<KeyboardMode>("mode");
|
|
for (const char *layout : {"us", "sk", "se", "de", "fr", "ru", "gr"}) {
|
|
QTest::addRow("%s compact", layout) << QString::fromLatin1(layout) << KeyboardMode::Compact;
|
|
QTest::addRow("%s full", layout) << QString::fromLatin1(layout) << KeyboardMode::Full;
|
|
}
|
|
}
|
|
|
|
void builtLayersHaveEvenRows()
|
|
{
|
|
QFETCH(QString, xkbLayout);
|
|
QFETCH(KeyboardMode, mode);
|
|
const Layout layout = TestLayouts::build(qPrintable(xkbLayout), mode);
|
|
for (const Layer &layer : layout.layers) {
|
|
const QList<QRectF> rects = computeKeyRects(layer, {1000, 300});
|
|
qsizetype i = 0;
|
|
for (const Row &row : layer.rows) {
|
|
const QRectF first = rects[i];
|
|
const QRectF last = rects[i + row.size() - 1];
|
|
QVERIFY2(qAbs(first.left() - rects.first().left()) < 0.01, qPrintable(layer.name));
|
|
QVERIFY2(qAbs(last.right() - rects[layer.rows.first().size() - 1].right()) < 0.01,
|
|
qPrintable(layer.name));
|
|
i += row.size();
|
|
}
|
|
}
|
|
}
|
|
|
|
void templateHasKeySections()
|
|
{
|
|
const Layout layout = TestLayouts::builtinTemplate();
|
|
QStringList ids;
|
|
for (const Section §ion : layout.sections)
|
|
ids.append(section.id);
|
|
QCOMPARE(ids, (QStringList{u"function"_s, u"navigation"_s, u"numpad"_s}));
|
|
}
|
|
|
|
void parsesKeysymKeys()
|
|
{
|
|
QString error;
|
|
const auto layout = LayoutLoader::parse(
|
|
R"({"id": "t", "layers": [{"name": "l", "rows": [[{"type": "keysym", "keysym": "Home", "repeat": true}]]}]})",
|
|
&error);
|
|
QVERIFY2(layout, qPrintable(error));
|
|
const KeyDef &key = layout->layers[0].rows[0][0];
|
|
QCOMPARE(key.keysym, quint32(XKB_KEY_Home));
|
|
QCOMPARE(key.label, u"Home"); // the keysym name unless labelled
|
|
QVERIFY(key.repeat);
|
|
}
|
|
|
|
void rejectsUnknownKeysym()
|
|
{
|
|
QString error;
|
|
QVERIFY(!LayoutLoader::parse(
|
|
R"({"id": "t", "layers": [{"name": "l", "rows": [[{"type": "keysym", "keysym": "Nope"}]]}]})", &error));
|
|
}
|
|
};
|
|
|
|
QTEST_GUILESS_MAIN(TestLayoutLoader)
|
|
#include "tst_layoutloader.moc"
|