Files
Crosspoint/src/activities/util/KeyboardEntryActivity.h
T
pablohc 53ca4f30d5 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 **_
2026-04-27 21:28:36 +02:00

227 lines
5.1 KiB
C++

#pragma once
#include <GfxRenderer.h>
#include <cstdint>
#include <functional>
#include <string>
#include <utility>
#include "../Activity.h"
#include "util/ButtonNavigator.h"
struct KeyDef {
char primary;
char secondary;
};
enum class SpecialKeyType { Shift, Mode, Space, Del, Ok };
enum class InputType { Text, Password, Url };
class KeyboardEntryActivity : public Activity {
public:
explicit KeyboardEntryActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
std::string title = "Enter Text", std::string initialText = "",
const size_t maxLength = 0, InputType inputType = InputType::Text)
: Activity("KeyboardEntry", renderer, mappedInput),
title(std::move(title)),
text(std::move(initialText)),
maxLength(maxLength),
inputType(inputType) {}
void onEnter() override;
void onExit() override;
void loop() override;
void render(RenderLock&&) override;
private:
std::string title;
std::string text;
size_t maxLength;
InputType inputType;
bool passwordVisible = false;
ButtonNavigator buttonNavigator;
int selectedRow = 0;
int selectedCol = 0;
int shiftState = 0;
bool symMode = false;
bool confirmHeld = false;
bool confirmLongHandled = false;
bool cursorMode = false;
bool togglePos = false;
size_t cursorPos = 0;
bool upHeld = false;
bool upLongHandled = false;
bool downHeld = false;
bool downLongHandled = false;
bool rightHeld = false;
bool rightLongHandled = false;
size_t savedCursorPos = 0;
size_t rightStartCursorPos = 0;
bool urlMode = false;
static constexpr int URL_SNIPPET_COUNT = 9;
static constexpr const char* const urlSnippets[URL_SNIPPET_COUNT] = {
"https://", "www.", ".com", "http://", "192.168.", ".org", "/opds", ":8080", ".net"};
int delPressCount = 0;
bool hintVisible = false;
unsigned long hintShowTime = 0;
void onComplete(std::string text);
void onCancel();
static constexpr uint16_t LONG_PRESS_MS = 500;
static constexpr uint16_t DEL_LONG_PRESS_MS = 1500;
static constexpr int COLS = 10;
static constexpr int ABC_ROWS = 4;
static constexpr int SYM_ROWS = 4;
static constexpr int BOTTOM_KEY_COUNT = 5;
static constexpr KeyDef abcLayout[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 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'},
{'3', '\0'},
{'4', '\0'},
{'5', '\0'},
{'6', '\0'},
{'7', '\0'},
{'8', '\0'},
{'9', '\0'},
{'0', '\0'}},
{{'!', '\0'},
{'@', '\0'},
{'#', '\0'},
{'$', '\0'},
{'%', '\0'},
{'^', '\0'},
{'&', '\0'},
{'*', '\0'},
{'(', '\0'},
{')', '\0'}},
{{'-', '\0'},
{'_', '\0'},
{'=', '\0'},
{'+', '\0'},
{'[', '\0'},
{']', '\0'},
{'{', '\0'},
{'}', '\0'},
{';', '\0'},
{':', '\0'}},
{{'\'', '\0'},
{'"', '\0'},
{'/', '\0'},
{'\\', '\0'},
{'|', '\0'},
{'?', '\0'},
{'.', '\0'},
{',', '\0'},
{'~', '\0'},
{'`', '\0'}},
};
static const char* const shiftString[2];
int getContentRowCount() const;
int getContentColCount() const;
int getTotalRowCount() const;
bool isBottomRow(int row) const;
char getSelectedChar() const;
char getAlternativeChar() const;
bool handleKeyPress();
bool insertChar(char c);
void insertString(const std::string& str);
void mapColContentBottom(int& col, bool goingUp) const;
};