mirror of
https://github.com/meshtastic/firmware.git
synced 2026-02-04 16:11:54 +00:00
* Added keyboard option to menu. Shows a keyboard layout but does not type. * Keyboard types into text box and wraps. * send FreeText messages from the send submenu - renamed `KEYBOARD` action to `FREE_TEXT` and moved its menu location to the send submenu - opening the FreeText applet from the menu keeps the menu open and disabled the timeout - the FreeText applet writes to inkhud->freetext - the sending a canned message checks inkhud->freetext and if it isn't empty, sends and clears the inkhud->freetext * Text scrolls along with input * handle free text message completion as an event implements `handleFreeText` and `OnFreeText()` for system applets to interface with the FreeText Applet The FreeText Applet generates an `OnFreeText` event when completing a message which is handled by the first system applet with the `handleFreeText` flag set to true. The Menu Applet now handles this event. * call `onFreeText` whenever the FreeText Applet exits allows the menu to consistently restart its auto-close timeout * Add text cursor * Change UI to remove the header and make text box longer Keyboard displays captial letters for legibility Keyboard types captial letters with long press * center FreeText keys and draw symbolic buttons Move input field and keyboard drawing to their own functions: - `drawInputField()` - `drawKeyboard()` Store the keys in a 1-dimensional array Implement a matching array, `keyWidths`, to set key widths relative to the font size * Add character limit and counter * Fix softlock when hitting character limit * Move text box as its own menu page * rework FreeTextApplet into KeyboardApplet - The Keyboard Applet renders an on-screen keyboard at the lower portion of the screen. - Calling `inkhud->openKeyboard()` sends all the user applets to the background and resizes the first system applet with `handleFreeText` set to True to fit above the on-screen keyboard - `inkhud->closeKeyboard()` reverses this layout change * Fix input box rendering and add character limit to menu free text * remove FREE_TEXT menu page and use the FREE_TEXT menu action solely * force update when changing the free text message * reorganize KeyboardApplet - add comments after each row of `key[]` and `keyWidths[]` to preserve formatting - The selected key is now set using the key index directly - rowWidths are pre-calculated in the KeyboardApplet constructor - removed `drawKeyboard()` and implemented `drawKeyLabel()` * implement `Renderer::clearTile()` to clear the region below a tile * add parameter to forceUpdate() for re-rendering the full screen setting the `all` parameter to true in `inkhud->forceUpdate()` now causes the full screen buffer to clear an re-render. This is helpful for when sending applets to the background and the UI needs a clean canvas. System Applets can now set the `alwaysRender` flag true which causes it to re-render on every screen update. This is set to true in the Battery Icon Applet. * clean up tile clearing loops * implement dirty rendering to let applets draw over their previous render - `Applet::requestUpdate()` now has an optional flag to keep the old canvas - If honored, the renderer calls `render(true)` which runs `onDirtyRender()` instead of `onRender()` for said applet - The renderer will not call a dirty render if the full screen is getting re-rendered * simplify arithmetic in clearTile for better understanding * combine Applet::onRender() and Applet::onDirtyRender() into Applet::onRender(bool full) - add new `full` parameter to onRender() in every applet. This parameter can be ignored by most applets. - `Applet::requestUpdate()` has an optional flag that requests a full render by default * implement tile and partial rendering in KeyboardApplet * add comment for drawKeyLabel() * improve clarity of byte operations in clearTile() * remove typo and commented code * fix inaccurate comments * add null check to openKeyboard() and closeKeyboard() --------- Co-authored-by: zeropt <ferr0fluidmann@gmail.com> Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
258 lines
8.4 KiB
C++
258 lines
8.4 KiB
C++
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
|
#include "./KeyboardApplet.h"
|
|
|
|
using namespace NicheGraphics;
|
|
|
|
InkHUD::KeyboardApplet::KeyboardApplet()
|
|
{
|
|
// Calculate row widths
|
|
for (uint8_t row = 0; row < KBD_ROWS; row++) {
|
|
rowWidths[row] = 0;
|
|
for (uint8_t col = 0; col < KBD_COLS; col++)
|
|
rowWidths[row] += keyWidths[row * KBD_COLS + col];
|
|
}
|
|
}
|
|
|
|
void InkHUD::KeyboardApplet::onRender(bool full)
|
|
{
|
|
uint16_t em = fontSmall.lineHeight(); // 16 pt
|
|
uint16_t keyH = Y(1.0) / KBD_ROWS;
|
|
int16_t keyTopPadding = (keyH - fontSmall.lineHeight()) / 2;
|
|
|
|
if (full) { // Draw full keyboard
|
|
for (uint8_t row = 0; row < KBD_ROWS; row++) {
|
|
|
|
// Calculate the remaining space to be used as padding
|
|
int16_t keyXPadding = X(1.0) - ((rowWidths[row] * em) >> 4);
|
|
|
|
// Draw keys
|
|
uint16_t xPos = 0;
|
|
for (uint8_t col = 0; col < KBD_COLS; col++) {
|
|
Color fgcolor = BLACK;
|
|
uint8_t index = row * KBD_COLS + col;
|
|
uint16_t keyX = ((xPos * em) >> 4) + ((col * keyXPadding) / (KBD_COLS - 1));
|
|
uint16_t keyY = row * keyH;
|
|
uint16_t keyW = (keyWidths[index] * em) >> 4;
|
|
if (index == selectedKey) {
|
|
fgcolor = WHITE;
|
|
fillRect(keyX, keyY, keyW, keyH, BLACK);
|
|
}
|
|
drawKeyLabel(keyX, keyY + keyTopPadding, keyW, keys[index], fgcolor);
|
|
xPos += keyWidths[index];
|
|
}
|
|
}
|
|
} else { // Only draw the difference
|
|
if (selectedKey != prevSelectedKey) {
|
|
// Draw previously selected key
|
|
uint8_t row = prevSelectedKey / KBD_COLS;
|
|
int16_t keyXPadding = X(1.0) - ((rowWidths[row] * em) >> 4);
|
|
uint16_t xPos = 0;
|
|
for (uint8_t i = prevSelectedKey - (prevSelectedKey % KBD_COLS); i < prevSelectedKey; i++)
|
|
xPos += keyWidths[i];
|
|
uint16_t keyX = ((xPos * em) >> 4) + (((prevSelectedKey % KBD_COLS) * keyXPadding) / (KBD_COLS - 1));
|
|
uint16_t keyY = row * keyH;
|
|
uint16_t keyW = (keyWidths[prevSelectedKey] * em) >> 4;
|
|
fillRect(keyX, keyY, keyW, keyH, WHITE);
|
|
drawKeyLabel(keyX, keyY + keyTopPadding, keyW, keys[prevSelectedKey], BLACK);
|
|
|
|
// Draw newly selected key
|
|
row = selectedKey / KBD_COLS;
|
|
keyXPadding = X(1.0) - ((rowWidths[row] * em) >> 4);
|
|
xPos = 0;
|
|
for (uint8_t i = selectedKey - (selectedKey % KBD_COLS); i < selectedKey; i++)
|
|
xPos += keyWidths[i];
|
|
keyX = ((xPos * em) >> 4) + (((selectedKey % KBD_COLS) * keyXPadding) / (KBD_COLS - 1));
|
|
keyY = row * keyH;
|
|
keyW = (keyWidths[selectedKey] * em) >> 4;
|
|
fillRect(keyX, keyY, keyW, keyH, BLACK);
|
|
drawKeyLabel(keyX, keyY + keyTopPadding, keyW, keys[selectedKey], WHITE);
|
|
}
|
|
}
|
|
|
|
prevSelectedKey = selectedKey;
|
|
}
|
|
|
|
// Draw the key label corresponding to the char
|
|
// for most keys it draws the character itself
|
|
// for ['\b', '\n', ' ', '\x1b'] it draws special glyphs
|
|
void InkHUD::KeyboardApplet::drawKeyLabel(uint16_t left, uint16_t top, uint16_t width, char key, Color color)
|
|
{
|
|
if (key == '\b') {
|
|
// Draw backspace glyph: 13 x 9 px
|
|
/**
|
|
* [][][][][][][][][]
|
|
* [][] []
|
|
* [][] [] [] []
|
|
* [][] [] [] []
|
|
* [][] [] []
|
|
* [][] [] [] []
|
|
* [][] [] [] []
|
|
* [][] []
|
|
* [][][][][][][][][]
|
|
*/
|
|
const uint8_t bsBitmap[] = {0x0f, 0xf8, 0x18, 0x08, 0x32, 0x28, 0x61, 0x48, 0xc0,
|
|
0x88, 0x61, 0x48, 0x32, 0x28, 0x18, 0x08, 0x0f, 0xf8};
|
|
uint16_t leftPadding = (width - 13) >> 1;
|
|
drawBitmap(left + leftPadding, top + 1, bsBitmap, 13, 9, color);
|
|
} else if (key == '\n') {
|
|
// Draw done glyph: 12 x 9 px
|
|
/**
|
|
* [][]
|
|
* [][]
|
|
* [][]
|
|
* [][]
|
|
* [][]
|
|
* [][] [][]
|
|
* [][] [][]
|
|
* [][][]
|
|
* []
|
|
*/
|
|
const uint8_t doneBitmap[] = {0x00, 0x30, 0x00, 0x60, 0x00, 0xc0, 0x01, 0x80, 0x03,
|
|
0x00, 0xc6, 0x00, 0x6c, 0x00, 0x38, 0x00, 0x10, 0x00};
|
|
uint16_t leftPadding = (width - 12) >> 1;
|
|
drawBitmap(left + leftPadding, top + 1, doneBitmap, 12, 9, color);
|
|
} else if (key == ' ') {
|
|
// Draw space glyph: 13 x 9 px
|
|
/**
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* [] []
|
|
* [] []
|
|
* [][][][][][][][][][][][][]
|
|
*
|
|
*
|
|
*/
|
|
const uint8_t spaceBitmap[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
|
0x08, 0x80, 0x08, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00};
|
|
uint16_t leftPadding = (width - 13) >> 1;
|
|
drawBitmap(left + leftPadding, top + 1, spaceBitmap, 13, 9, color);
|
|
} else if (key == '\x1b') {
|
|
setTextColor(color);
|
|
std::string keyText = "ESC";
|
|
uint16_t leftPadding = (width - getTextWidth(keyText)) >> 1;
|
|
printAt(left + leftPadding, top, keyText);
|
|
} else {
|
|
setTextColor(color);
|
|
if (key >= 0x61)
|
|
key -= 32; // capitalize
|
|
std::string keyText = std::string(1, key);
|
|
uint16_t leftPadding = (width - getTextWidth(keyText)) >> 1;
|
|
printAt(left + leftPadding, top, keyText);
|
|
}
|
|
}
|
|
|
|
void InkHUD::KeyboardApplet::onForeground()
|
|
{
|
|
handleInput = true; // Intercept the button input for our applet
|
|
|
|
// Select the first key
|
|
selectedKey = 0;
|
|
prevSelectedKey = 0;
|
|
}
|
|
|
|
void InkHUD::KeyboardApplet::onBackground()
|
|
{
|
|
handleInput = false;
|
|
}
|
|
|
|
void InkHUD::KeyboardApplet::onButtonShortPress()
|
|
{
|
|
char key = keys[selectedKey];
|
|
if (key == '\n') {
|
|
inkhud->freeTextDone();
|
|
inkhud->closeKeyboard();
|
|
} else if (key == '\x1b') {
|
|
inkhud->freeTextCancel();
|
|
inkhud->closeKeyboard();
|
|
} else {
|
|
inkhud->freeText(key);
|
|
}
|
|
}
|
|
|
|
void InkHUD::KeyboardApplet::onButtonLongPress()
|
|
{
|
|
char key = keys[selectedKey];
|
|
if (key == '\n') {
|
|
inkhud->freeTextDone();
|
|
inkhud->closeKeyboard();
|
|
} else if (key == '\x1b') {
|
|
inkhud->freeTextCancel();
|
|
inkhud->closeKeyboard();
|
|
} else {
|
|
if (key >= 0x61)
|
|
key -= 32; // capitalize
|
|
inkhud->freeText(key);
|
|
}
|
|
}
|
|
|
|
void InkHUD::KeyboardApplet::onExitShort()
|
|
{
|
|
inkhud->freeTextCancel();
|
|
inkhud->closeKeyboard();
|
|
}
|
|
|
|
void InkHUD::KeyboardApplet::onExitLong()
|
|
{
|
|
inkhud->freeTextCancel();
|
|
inkhud->closeKeyboard();
|
|
}
|
|
|
|
void InkHUD::KeyboardApplet::onNavUp()
|
|
{
|
|
if (selectedKey < KBD_COLS) // wrap
|
|
selectedKey += KBD_COLS * (KBD_ROWS - 1);
|
|
else // move 1 row back
|
|
selectedKey -= KBD_COLS;
|
|
|
|
// Request rendering over the previously drawn render
|
|
requestUpdate(EInk::UpdateTypes::FAST, false);
|
|
// Force an update to bypass lockRequests
|
|
inkhud->forceUpdate(EInk::UpdateTypes::FAST);
|
|
}
|
|
|
|
void InkHUD::KeyboardApplet::onNavDown()
|
|
{
|
|
selectedKey += KBD_COLS;
|
|
selectedKey %= (KBD_COLS * KBD_ROWS);
|
|
|
|
// Request rendering over the previously drawn render
|
|
requestUpdate(EInk::UpdateTypes::FAST, false);
|
|
// Force an update to bypass lockRequests
|
|
inkhud->forceUpdate(EInk::UpdateTypes::FAST);
|
|
}
|
|
|
|
void InkHUD::KeyboardApplet::onNavLeft()
|
|
{
|
|
if (selectedKey % KBD_COLS == 0) // wrap
|
|
selectedKey += KBD_COLS - 1;
|
|
else // move 1 column back
|
|
selectedKey--;
|
|
|
|
// Request rendering over the previously drawn render
|
|
requestUpdate(EInk::UpdateTypes::FAST, false);
|
|
// Force an update to bypass lockRequests
|
|
inkhud->forceUpdate(EInk::UpdateTypes::FAST);
|
|
}
|
|
|
|
void InkHUD::KeyboardApplet::onNavRight()
|
|
{
|
|
if (selectedKey % KBD_COLS == KBD_COLS - 1) // wrap
|
|
selectedKey -= KBD_COLS - 1;
|
|
else // move 1 column forward
|
|
selectedKey++;
|
|
|
|
// Request rendering over the previously drawn render
|
|
requestUpdate(EInk::UpdateTypes::FAST, false);
|
|
// Force an update to bypass lockRequests
|
|
inkhud->forceUpdate(EInk::UpdateTypes::FAST);
|
|
}
|
|
|
|
uint16_t InkHUD::KeyboardApplet::getKeyboardHeight()
|
|
{
|
|
const uint16_t keyH = fontSmall.lineHeight() * 1.2;
|
|
return keyH * KBD_ROWS;
|
|
}
|
|
#endif
|