Optional PC key blocks: function row, navigation, numeric keypad
- Template sections placed above or to the right of the typing keys; the user picks any mix in settings, typing keys included - keysym keys send XKB keysyms on press, optionally repeating - Geometry lays blocks side by side with one key unit; stretch keys are resolved at layout time so composed blocks line up - Toggling a block resizes the window so keys keep their size Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,7 @@ private Q_SLOTS:
|
||||
{
|
||||
engine = {};
|
||||
model.setLayout(TestLayouts::build("us"));
|
||||
model.setVisibleSections({MainSectionId});
|
||||
controller = std::make_unique<KeyboardController>(engine, model);
|
||||
}
|
||||
|
||||
@@ -199,6 +200,36 @@ private Q_SLOTS:
|
||||
QCOMPARE(requested, 1);
|
||||
}
|
||||
|
||||
void keysymKeyActsOnPressAndRepeats()
|
||||
{
|
||||
KeyDef left = makeKey(KeyType::Keysym, u"←"_s);
|
||||
left.keysym = XKB_KEY_Left;
|
||||
left.repeat = true;
|
||||
controller->keyDown(left, 0);
|
||||
QCOMPARE(engine.keysyms, QList<quint32>{XKB_KEY_Left});
|
||||
QTRY_VERIFY_WITH_TIMEOUT(engine.keysyms.size() >= 3, 2000); // held: repeats
|
||||
controller->keyUp(left, KeyOutput::Primary, {}, 0);
|
||||
const auto sent = engine.keysyms.size();
|
||||
QTest::qWait(200);
|
||||
QCOMPARE(engine.keysyms.size(), sent);
|
||||
QVERIFY(engine.output.isEmpty()); // no text
|
||||
}
|
||||
|
||||
void sectionsAreComposedAroundTheMainKeys()
|
||||
{
|
||||
const qsizetype mainKeys = model.rowCount();
|
||||
const qreal mainUnits = model.totalUnits();
|
||||
model.setVisibleSections({MainSectionId, u"function"_s});
|
||||
QCOMPARE(model.rowCount(), mainKeys + 13);
|
||||
QCOMPARE(model.totalUnits(), mainUnits); // above, not beside
|
||||
model.setVisibleSections({MainSectionId, u"navigation"_s});
|
||||
QCOMPARE(model.totalUnits(), mainUnits + 3);
|
||||
model.setVisibleSections({u"numpad"_s});
|
||||
QCOMPARE(model.rowCount(), 19);
|
||||
model.setVisibleSections({});
|
||||
QCOMPARE(model.rowCount(), mainKeys); // never nothing
|
||||
}
|
||||
|
||||
void dismissEmitsSignal()
|
||||
{
|
||||
int dismissed = 0;
|
||||
|
||||
@@ -49,6 +49,26 @@ private Q_SLOTS:
|
||||
QCOMPARE(rects[4].left(), 100.0);
|
||||
QCOMPARE(rects[5].right(), 300.0);
|
||||
}
|
||||
|
||||
void stretchKeysFillTheRow()
|
||||
{
|
||||
KeyDef wide = key();
|
||||
wide.stretch = true;
|
||||
const Layer layer{u"l"_s, {{key(), key(), key(), key()}, {key(), wide}}};
|
||||
const auto rects = computeKeyRects(layer, {400, 100}, {.padding = 0, .gap = 0});
|
||||
QCOMPARE(rects[4].left(), 0.0);
|
||||
QCOMPARE(rects[5].width(), 300.0);
|
||||
}
|
||||
|
||||
void blocksShareUnitAndSitAtTheBottom()
|
||||
{
|
||||
const Block tall{{key(), key()}, {key(), key()}};
|
||||
const Block shortBlock{{key()}};
|
||||
const auto rects = computeKeyRects({tall, shortBlock}, {400, 100}, {.padding = 0, .gap = 0, .blockGap = 100});
|
||||
// 3 units in 300 px: 100 each.
|
||||
QCOMPARE(rects[1].right(), 200.0);
|
||||
QCOMPARE(rects[4], QRectF(300, 50, 100, 50));
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(TestKeyGeometry)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "layout/LayoutLoader.h"
|
||||
|
||||
#include <QTest>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
@@ -67,24 +68,60 @@ private Q_SLOTS:
|
||||
&error));
|
||||
}
|
||||
|
||||
// Stretching must even out rows whatever the keymap's letter counts.
|
||||
void builtLayersHaveEqualRows_data()
|
||||
// Stretch keys must even out rows whatever the keymap's letter counts.
|
||||
void builtLayersHaveEvenRows_data()
|
||||
{
|
||||
QTest::addColumn<QString>("xkbLayout");
|
||||
for (const char *layout : {"us", "sk", "se", "de", "fr", "ru", "gr"})
|
||||
QTest::newRow(layout) << QString::fromLatin1(layout);
|
||||
}
|
||||
|
||||
void builtLayersHaveEqualRows()
|
||||
void builtLayersHaveEvenRows()
|
||||
{
|
||||
QFETCH(QString, xkbLayout);
|
||||
const Layout layout = TestLayouts::build(qPrintable(xkbLayout));
|
||||
for (const Layer &layer : layout.layers) {
|
||||
const qreal expected = rowUnits(layer.rows.first());
|
||||
for (const Row &row : layer.rows)
|
||||
QVERIFY2(qFuzzyCompare(rowUnits(row), expected), qPrintable(layer.name));
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user