- 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>
47 lines
1.3 KiB
C++
47 lines
1.3 KiB
C++
#include "KeyGeometry.h"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace {
|
|
|
|
qreal rowUnits(const Row &row)
|
|
{
|
|
qreal units = 0;
|
|
for (const KeyDef &key : row)
|
|
units += key.width;
|
|
return units;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
QList<QRectF> computeKeyRects(const Layer &layer, QSizeF size, KeyboardMetrics metrics)
|
|
{
|
|
QList<QRectF> rects;
|
|
if (layer.rows.isEmpty())
|
|
return rects;
|
|
|
|
qreal maxUnits = 0;
|
|
for (const Row &row : layer.rows)
|
|
maxUnits = std::max(maxUnits, rowUnits(row));
|
|
|
|
const qreal innerWidth = size.width() - 2 * metrics.padding;
|
|
const qreal innerHeight = size.height() - 2 * metrics.padding;
|
|
const int rowCount = int(layer.rows.size());
|
|
|
|
// Each cell includes one trailing gap; the last gap of a row is dropped.
|
|
const qreal unitWidth = (innerWidth + metrics.gap) / maxUnits;
|
|
const qreal rowHeight = (innerHeight - (rowCount - 1) * metrics.gap) / rowCount;
|
|
|
|
qreal y = metrics.padding;
|
|
for (const Row &row : layer.rows) {
|
|
qreal x = metrics.padding + (maxUnits - rowUnits(row)) * unitWidth / 2;
|
|
for (const KeyDef &key : row) {
|
|
const qreal cellWidth = key.width * unitWidth;
|
|
rects.append(QRectF(x, y, std::max<qreal>(0, cellWidth - metrics.gap), rowHeight));
|
|
x += cellWidth;
|
|
}
|
|
y += rowHeight + metrics.gap;
|
|
}
|
|
return rects;
|
|
}
|