fix: keyboard feedback #1644 (#1697)

Addresses reviewer feedback from #1644:
- **Localize keyboard hint strings** — 13 hardcoded English strings
replaced with \`tr()\` macro (\`STR_KB_HINT_*\`), making them
translatable across all 22 languages (fallback to English when not yet
translated)
- **Deduplicate \`Lyra3CoversMetrics\`** — now derives from
\`LyraMetrics\` via lambda copy, overriding only \`homeCoverTileHeight\`
and \`homeRecentBooksCount\` (eliminates ~30 duplicated metric fields)
- **Unify keyboard drawing in \`BaseTheme\`** — \`drawTextField\` and
\`drawKeyboardKey\` overrides removed from \`LyraTheme\`; variability
controlled via \`keyboardKeyCornerRadius\` metric (0=Base, 6=Lyra).
Unified text field padding to 6, adopted Lyra's secondary label draw
order (main first, then secondary)
- **Add URL-optimized keyboard layout** — \`urlLayout\` with \`:\` and
\`/\` replacing \`=\` and \`,\` for easier URL input without switching
to SYM mode
---

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _** YES **_
This commit is contained in:
pablohc
2026-04-27 21:28:36 +02:00
committed by jpirnay
parent 875eb29215
commit 53ca4f30d5
3 changed files with 83 additions and 23 deletions
+25 -23
View File
@@ -49,7 +49,7 @@ int KeyboardEntryActivity::getTotalRowCount() const { return getContentRowCount(
bool KeyboardEntryActivity::isBottomRow(const int row) const { return row == getContentRowCount(); }
char KeyboardEntryActivity::getSelectedChar() const {
const KeyDef(*layout)[COLS] = symMode ? symLayout : abcLayout;
const KeyDef(*layout)[COLS] = symMode ? symLayout : (inputType == InputType::Url ? urlLayout : abcLayout);
if (selectedRow < 0 || selectedRow >= getContentRowCount()) return '\0';
if (selectedCol < 0 || selectedCol >= COLS) return '\0';
@@ -526,20 +526,22 @@ void KeyboardEntryActivity::render(RenderLock&&) {
const int hintY = underlineY + 4;
if (cursorMode) {
int hintLineY = hintY;
renderer.drawCenteredText(SMALL_FONT_ID, hintLineY, "Press < or > to move cursor", true);
hintLineY += hintLh;
if (inputType == InputType::Password) {
const char* passTip;
if (togglePos) {
passTip = "Press < to return to cursor position";
} else {
passTip =
passwordVisible ? "Hold > then press [***] to hide password" : "Hold > then press [abc] to show password";
if (inputType == InputType::Password && togglePos) {
renderer.drawCenteredText(
SMALL_FONT_ID, hintLineY,
passwordVisible ? tr(STR_KB_HINT_TOGGLE_HIDE_PASSWORD) : tr(STR_KB_HINT_TOGGLE_SHOW_PASSWORD), true);
hintLineY += hintLh;
renderer.drawCenteredText(SMALL_FONT_ID, hintLineY, tr(STR_KB_HINT_RETURN_CURSOR), true);
} else {
renderer.drawCenteredText(SMALL_FONT_ID, hintLineY, tr(STR_KB_HINT_MOVE_CURSOR), true);
hintLineY += hintLh;
if (inputType == InputType::Password) {
const char* passTip = passwordVisible ? tr(STR_KB_HINT_HIDE_PASSWORD) : tr(STR_KB_HINT_SHOW_PASSWORD);
renderer.drawCenteredText(SMALL_FONT_ID, hintLineY, passTip, true);
}
renderer.drawCenteredText(SMALL_FONT_ID, hintLineY, passTip, true);
}
} else {
renderer.drawCenteredText(SMALL_FONT_ID, hintY, "Hold UP to edit entry", true);
renderer.drawCenteredText(SMALL_FONT_ID, hintY, tr(STR_KB_HINT_EDIT_ENTRY), true);
}
}
@@ -575,37 +577,37 @@ void KeyboardEntryActivity::render(RenderLock&&) {
if (tipCount > 0) {
int y = (underlineBottom + keyboardStartY) / 2 - (tipCount + 1) * tipsLh / 2;
drawTip("Tips:", y);
drawTip(tr(STR_KB_TIPS), y);
y += tipsLh;
if (cursorMode) {
drawTip("Press DOWN to return to keyboard", y);
drawTip(tr(STR_KB_HINT_RETURN_KEYBOARD), y);
} else if (urlMode) {
drawTip("Press ABC to exit URL mode", y);
drawTip(tr(STR_KB_HINT_EXIT_URL_MODE), y);
y += tipsLh;
if (!text.empty()) {
drawTip("Hold DEL to clear all text", y);
drawTip(tr(STR_KB_HINT_CLEAR_TEXT), y);
}
} else if (symMode) {
if (!text.empty()) {
drawTip("Hold DEL to clear all text", y);
drawTip(tr(STR_KB_HINT_CLEAR_TEXT), y);
}
} else {
const char* altCharTip;
if (inputType == InputType::Url) {
altCharTip = "Hold SELECT for secondary char";
altCharTip = tr(STR_KB_HINT_SECONDARY_CHAR);
} else if (shiftState > 0) {
altCharTip = "Hold SELECT for lowercase or secondary char";
altCharTip = tr(STR_KB_HINT_LOWER_SECONDARY);
} else {
altCharTip = "Hold SELECT for UPPERCASE or secondary char";
altCharTip = tr(STR_KB_HINT_UPPER_SECONDARY);
}
drawTip(altCharTip, y);
y += tipsLh;
if (inputType == InputType::Url) {
drawTip("Press URL for snippets", y);
drawTip(tr(STR_KB_HINT_URL_SNIPPETS), y);
y += tipsLh;
}
if (!text.empty()) {
drawTip("Hold DEL to clear all text", y);
drawTip(tr(STR_KB_HINT_CLEAR_TEXT), y);
}
}
}
@@ -625,7 +627,7 @@ void KeyboardEntryActivity::render(RenderLock&&) {
urlLeftMargin = urlCenterX - urlTotalWidth / 2;
}
const KeyDef(*layout)[COLS] = symMode ? symLayout : abcLayout;
const KeyDef(*layout)[COLS] = symMode ? symLayout : (inputType == InputType::Url ? urlLayout : abcLayout);
const int contentRows = getContentRowCount();
for (int row = 0; row < contentRows; row++) {
@@ -125,6 +125,49 @@ class KeyboardEntryActivity : public Activity {
{',', '<'}},
};
static constexpr KeyDef urlLayout[ABC_ROWS][COLS] = {
{{'1', '!'},
{'2', '@'},
{'3', '#'},
{'4', '$'},
{'5', '%'},
{'6', '^'},
{'7', '&'},
{'8', '*'},
{'9', '('},
{'0', ')'}},
{{'q', 'Q'},
{'w', 'W'},
{'e', 'E'},
{'r', 'R'},
{'t', 'T'},
{'y', 'Y'},
{'u', 'U'},
{'i', 'I'},
{'o', 'O'},
{'p', 'P'}},
{{'a', 'A'},
{'s', 'S'},
{'d', 'D'},
{'f', 'F'},
{'g', 'G'},
{'h', 'H'},
{'j', 'J'},
{'k', 'K'},
{'l', 'L'},
{'-', '_'}},
{{'z', 'Z'},
{'x', 'X'},
{'c', 'C'},
{'v', 'V'},
{'b', 'B'},
{'n', 'N'},
{'m', 'M'},
{':', '+'},
{'.', '>'},
{'/', '<'}},
};
static constexpr KeyDef symLayout[SYM_ROWS][COLS] = {
{{'1', '\0'},
{'2', '\0'},