Fix double-space period, shift double-tap; settings; letters from system keymap

- Double-space replaced the space via a Backspace keysym, which clients
  apply after the commit: use delete_surrounding_text with the commit
- Tests use a shared FakeEngine that applies ops to a simulated field
- Double-tap shift locks at a sentence start; typing between taps doesn't
- Organization name set: config lives in ~/.config/sloposk/
- Settings (auto-capitalization, double-space period) with a ⚙ panel
- Letter rows come from the compositor's XKB keymap; other letters of the
  layout become long-press alternates; globe switches the system layout
  over org.kde.keyboard
- One JSON template for non-letter layers; stretch keys even out rows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 20:28:39 +02:00
co-authored by Claude Opus 5.5
parent c6c977eb4c
commit 5277ab16ff
49 changed files with 1162 additions and 359 deletions
+29 -29
View File
@@ -1,22 +1,11 @@
#include "TestLayouts.h"
#include "layout/KeyGeometry.h"
#include "layout/LayoutLoader.h"
#include "layout/LayoutRepository.h"
#include <QTest>
using namespace Qt::StringLiterals;
namespace {
qreal rowUnits(const Row &row)
{
qreal units = 0;
for (const KeyDef &key : row)
units += key.width;
return units;
}
} // namespace
class TestLayoutLoader : public QObject
{
Q_OBJECT
@@ -65,24 +54,35 @@ private Q_SLOTS:
QVERIFY(!LayoutLoader::validate(*layout, &error));
}
void builtinLayoutsAreConsistent()
void templateIsValid()
{
Q_INIT_RESOURCE(layouts);
LayoutRepository repo;
repo.loadDirectory(u":/layouts"_s);
repo.finalize({});
QVERIFY(repo.indexOf(u"en-US"_s) >= 0);
QVERIFY(repo.indexOf(u"sv-SE"_s) >= 0);
QString error;
QVERIFY2(LayoutLoader::validate(TestLayouts::builtinTemplate(), &error), qPrintable(error));
}
for (const Layout &layout : repo.layouts()) {
QVERIFY2(layout.layer(u"numbers"_s), qPrintable(layout.id)); // sv-SE gets it from its base
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(u"%1/%2: rows differ in width"_s.arg(layout.id, layer.name)));
}
}
void rejectsLettersWithoutRow()
{
QString error;
QVERIFY(!LayoutLoader::parse(R"({"id": "t", "layers": [{"name": "l", "rows": [[{"type": "letters"}]]}]})",
&error));
}
// Stretching must even out rows whatever the keymap's letter counts.
void builtLayersHaveEqualRows_data()
{
QTest::addColumn<QString>("xkbLayout");
for (const char *layout : {"us", "sk", "se", "de", "fr", "ru", "gr"})
QTest::newRow(layout) << QString::fromLatin1(layout);
}
void builtLayersHaveEqualRows()
{
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));
}
}
};