Layouts, iPad-style rendering, taps, shift and layers
- 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>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user