- JSON layouts (en-US, sv-SE inheriting number/symbol layers via base) - LayoutModel + KeyGeometry: proportional rows, nearest-key hit testing - KeyboardSurface: multi-pointer touch/mouse routing to the controller - KeyboardController + ShiftState: iOS shift/caps-lock, layers, globe, dismiss - QML view with light/dark iPad palette and drag handle - DevHooks: SLOPOSK_AUTOTYPE, SLOPOSK_SCREENSHOT for headless checks - Unit tests for shift, loader, geometry and controller Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
2.1 KiB
C++
76 lines
2.1 KiB
C++
#include "LayoutRepository.h"
|
|
#include "LayoutLoader.h"
|
|
|
|
#include <QDir>
|
|
#include <QLoggingCategory>
|
|
|
|
#include <algorithm>
|
|
|
|
Q_LOGGING_CATEGORY(lcLayouts, "sloposk.layouts")
|
|
|
|
void LayoutRepository::loadDirectory(const QString &dir)
|
|
{
|
|
const QDir directory(dir);
|
|
for (const QString &file : directory.entryList({QStringLiteral("*.json")}, QDir::Files, QDir::Name)) {
|
|
QString error;
|
|
const auto layout = LayoutLoader::load(directory.filePath(file), &error);
|
|
if (layout)
|
|
add(*layout);
|
|
else
|
|
qCWarning(lcLayouts) << "Skipping" << directory.filePath(file) << ":" << error;
|
|
}
|
|
}
|
|
|
|
void LayoutRepository::finalize(const QString &preferredLocale)
|
|
{
|
|
resolveBases();
|
|
|
|
m_layouts.removeIf([](const Layout &layout) {
|
|
QString error;
|
|
if (LayoutLoader::validate(layout, &error))
|
|
return false;
|
|
qCWarning(lcLayouts) << "Dropping layout" << layout.id << ":" << error;
|
|
return true;
|
|
});
|
|
|
|
std::stable_partition(m_layouts.begin(), m_layouts.end(), [&](const Layout &layout) {
|
|
return layout.locale == preferredLocale;
|
|
});
|
|
}
|
|
|
|
int LayoutRepository::indexOf(const QString &id) const
|
|
{
|
|
for (qsizetype i = 0; i < m_layouts.size(); ++i) {
|
|
if (m_layouts[i].id == id)
|
|
return int(i);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void LayoutRepository::add(const Layout &layout)
|
|
{
|
|
const int existing = indexOf(layout.id);
|
|
if (existing >= 0)
|
|
m_layouts[existing] = layout;
|
|
else
|
|
m_layouts.append(layout);
|
|
}
|
|
|
|
void LayoutRepository::resolveBases()
|
|
{
|
|
// One level of inheritance keeps this simple and cycle-free.
|
|
for (Layout &layout : m_layouts) {
|
|
if (layout.base.isEmpty())
|
|
continue;
|
|
const int baseIndex = indexOf(layout.base);
|
|
if (baseIndex < 0) {
|
|
qCWarning(lcLayouts) << "Layout" << layout.id << "has unknown base" << layout.base;
|
|
continue;
|
|
}
|
|
for (const Layer &layer : m_layouts[baseIndex].layers) {
|
|
if (!layout.layer(layer.name))
|
|
layout.layers.append(layer);
|
|
}
|
|
}
|
|
}
|