Keep blocks still when switching layers
- The main block reserves the width and rows of its biggest layer, so the numpad and function row no longer shift between letters and symbols - Smaller layers stretch into the room: stretch keys widen, rows grow taller (rows above the layer, like the function row, keep their height) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@ namespace {
|
||||
|
||||
qreal blockUnits(const Block &block)
|
||||
{
|
||||
qreal units = 0;
|
||||
qreal units = block.minUnits;
|
||||
for (const Row &row : block.rows)
|
||||
units = std::max(units, rowUnits(row));
|
||||
return units;
|
||||
@@ -49,7 +49,7 @@ qsizetype totalRows(const QList<Block> &blocks)
|
||||
qsizetype rows = 0;
|
||||
for (const Block &block : blocks) {
|
||||
if (allFill || !block.fillHeight)
|
||||
rows = std::max(rows, block.rows.size());
|
||||
rows = std::max({rows, block.rows.size(), block.minRows});
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
@@ -69,17 +69,24 @@ QList<QRectF> computeKeyRects(const QList<Block> &blocks, QSizeF size, KeyboardM
|
||||
// Each cell includes one trailing gap; the last gap of a row is dropped,
|
||||
// and blocks are separated by blockGap instead.
|
||||
const qreal unitWidth = (innerWidth + blockCount * metrics.gap - (blockCount - 1) * metrics.blockGap) / units;
|
||||
const auto heightFor = [&](qsizetype rows) { return (innerHeight - qreal(rows - 1) * metrics.gap) / qreal(rows); };
|
||||
const qreal sharedRowHeight = heightFor(maxRows);
|
||||
const qreal sharedRowHeight = (innerHeight - qreal(maxRows - 1) * metrics.gap) / qreal(maxRows);
|
||||
const auto spanOf = [&](qsizetype rows) { return qreal(rows) * (sharedRowHeight + metrics.gap) - metrics.gap; };
|
||||
|
||||
qreal left = metrics.padding;
|
||||
for (const Block &block : blocks) {
|
||||
const qreal width = blockUnits(block);
|
||||
const qsizetype rows = block.rows.size();
|
||||
const bool fill = block.fillHeight && rows > 0;
|
||||
const qreal rowHeight = fill ? heightFor(rows) : sharedRowHeight;
|
||||
qreal y = metrics.padding + (fill ? 0 : qreal(maxRows - rows) * (rowHeight + metrics.gap));
|
||||
for (const Row &row : block.rows) {
|
||||
const auto rows = block.rows.size();
|
||||
const qreal span = block.fillHeight ? innerHeight : spanOf(std::max(rows, block.minRows));
|
||||
const qsizetype fixed = std::min(block.fixedRows, rows);
|
||||
// Rows after the fixed ones share what's left; with none, the block sits at the bottom.
|
||||
const qreal flexHeight = rows > fixed
|
||||
? (span - qreal(fixed) * (sharedRowHeight + metrics.gap) - qreal(rows - fixed - 1) * metrics.gap)
|
||||
/ qreal(rows - fixed)
|
||||
: sharedRowHeight;
|
||||
qreal y = metrics.padding + innerHeight - (rows > fixed ? span : spanOf(rows));
|
||||
for (qsizetype r = 0; r < rows; ++r) {
|
||||
const Row &row = block.rows[r];
|
||||
const qreal rowHeight = r < fixed ? sharedRowHeight : flexHeight;
|
||||
const QList<qreal> widths = stretchedWidths(row, width);
|
||||
const qreal used = std::accumulate(widths.begin(), widths.end(), 0.0);
|
||||
qreal x = left + (width - used) * unitWidth / 2;
|
||||
|
||||
@@ -12,16 +12,23 @@ struct KeyboardMetrics {
|
||||
};
|
||||
|
||||
// The keys on screen: blocks of rows, side by side.
|
||||
// A block can reserve room (minUnits, minRows) so that swapping its rows, e.g.
|
||||
// switching layers, moves nothing else; its keys stretch into the room.
|
||||
struct Block {
|
||||
QList<Row> rows;
|
||||
bool fillHeight = false; // rows grow to span the full height instead of sharing the row height
|
||||
bool fillHeight = false; // spans the full height instead of max(rows, minRows) shared rows
|
||||
qreal minUnits = 0; // reserved width
|
||||
qsizetype minRows = 0; // reserved height, in shared rows
|
||||
qsizetype fixedRows = 0; // leading rows that keep the shared height when the block stretches
|
||||
};
|
||||
|
||||
// Lays out blocks to fill `size`. Returns one rect per key, block by block in
|
||||
// row order (spacers included). One key unit has the same width everywhere;
|
||||
// rows share one height, set by the tallest block that doesn't fill, and
|
||||
// shorter blocks sit at the bottom. In each block, stretch keys grow to make
|
||||
// their row as wide as the widest one, and remaining shorter rows are centered.
|
||||
// shorter blocks sit at the bottom. A block given more height than its rows
|
||||
// need (fillHeight, minRows) grows its rows after fixedRows to take it up.
|
||||
// In each block, stretch keys grow to make their row as wide as the widest
|
||||
// one (or minUnits), and remaining shorter rows are centered.
|
||||
QList<QRectF> computeKeyRects(const QList<Block> &blocks, QSizeF size, KeyboardMetrics metrics = {});
|
||||
inline QList<QRectF> computeKeyRects(const Layer &layer, QSizeF size, KeyboardMetrics metrics = {})
|
||||
{
|
||||
@@ -32,5 +39,5 @@ inline QList<QRectF> computeKeyRects(const Layer &layer, QSizeF size, KeyboardMe
|
||||
qreal rowUnits(const Row &row);
|
||||
// Width of all blocks in key units, ignoring gaps.
|
||||
qreal totalUnits(const QList<Block> &blocks);
|
||||
// Rows of the tallest block that sets the row height (see computeKeyRects).
|
||||
// Shared rows of the tallest block that sets the row height (see computeKeyRects).
|
||||
qsizetype totalRows(const QList<Block> &blocks);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "LayoutModel.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
@@ -183,8 +184,16 @@ QList<Block> LayoutModel::composeBlocks() const
|
||||
if (section.placement == Section::Placement::Above && shown(section.id))
|
||||
main.rows += section.rows;
|
||||
}
|
||||
if (layer && shown(MainSectionId))
|
||||
if (layer && shown(MainSectionId)) {
|
||||
// Room for the biggest layer, so switching layers keeps the other blocks still.
|
||||
main.fixedRows = main.rows.size();
|
||||
for (const Layer &other : m_layout.layers) {
|
||||
main.minRows = std::max(main.minRows, main.fixedRows + other.rows.size());
|
||||
for (const Row &row : other.rows)
|
||||
main.minUnits = std::max(main.minUnits, rowUnits(row));
|
||||
}
|
||||
main.rows += layer->rows;
|
||||
}
|
||||
|
||||
QList<Block> blocks;
|
||||
if (!main.rows.isEmpty())
|
||||
|
||||
Reference in New Issue
Block a user