/* ESPboyTerminalGUI class for www.ESPboy.com project by RomanS https://hackaday.io/project/164830-espboy-games-iot-stem-for-education-fun v2.1 */ #include "ESPboyTerminalGUI.h" #define SOUNDPIN D3 const uint8_t ESPboyTerminalGUI::keybOnscr[2][3][21] PROGMEM = { {"+1234567890abcdefghi", "jklmnopqrstuvwxyz -=", "?!@$%&*()_[]\":;.,^begin(*tft); u8f->setFontMode(1); // use u8g2 none transparent mode u8f->setBackgroundColor(TFT_BLACK); u8f->setFontDirection(0); // left to right u8f->setFont(u8g2_font_4x6_t_cyrillic); #endif consoleStringsVector.push_back(consoleStringS()); consoleStringsVector.back().consoleString = ""; consoleStringsVector.back().consoleStringColor = TFT_BLACK; toggleDisplayMode(1); } uint8_t ESPboyTerminalGUI::keysAction() { uint8_t longActPress = 0; uint8_t keyState = getKeys(); if (keyState) { #ifdef buttonclicks tone(SOUNDPIN, 100, 10); #endif if (!keybParam.displayMode) { if (keyState & GUI_PAD_LEFT && keyState & GUI_PAD_UP) { // shift keybParam.shiftOn = !keybParam.shiftOn; drawKeyboard(keybParam.selX, keybParam.selY, 0); waitKeyUnpressed(); } else { if ((keyState & GUI_PAD_RIGHT) && keybParam.selX < 20) keybParam.selX++; if ((keyState & GUI_PAD_LEFT) && keybParam.selX > -1) keybParam.selX--; if ((keyState & GUI_PAD_DOWN) && keybParam.selY < 3) keybParam.selY++; if ((keyState & GUI_PAD_UP) && keybParam.selY > -1) keybParam.selY--; if ((keyState & GUI_PAD_LEFT) && keybParam.selX == -1) keybParam.selX = 19; if ((keyState & GUI_PAD_RIGHT) && keybParam.selX == 20) keybParam.selX = 0; if ((keyState & GUI_PAD_UP) && keybParam.selY == -1) keybParam.selY = 2; if ((keyState & GUI_PAD_DOWN) && keybParam.selY == 3) keybParam.selY = 0; } if ((keyState&GUI_PAD_ACT && keyState&GUI_PAD_ESC) || (keyState&GUI_PAD_RGT && keyState&GUI_PAD_LFT)) { if (keybParam.renderLine > consoleStringsVector.size() - GUI_MAX_STRINGS_ONSCREEN_FULL) keybParam.renderLine = consoleStringsVector.size() - GUI_MAX_STRINGS_ONSCREEN_FULL; toggleDisplayMode(1); waitKeyUnpressed(); } else if (keyState&GUI_PAD_RGT && keybParam.renderLine) { keybParam.renderLine--; drawConsole(0); } else if (keyState&GUI_PAD_LFT && keybParam.renderLine < consoleStringsVector.size() - GUI_MAX_STRINGS_ONSCREEN_SMALL) { keybParam.renderLine++; drawConsole(0); } if ((((keyState & GUI_PAD_ACT) && (keybParam.selX == 19 && keybParam.selY == 2)) || (keyState & GUI_PAD_RGT && keyState & GUI_PAD_LFT))) { // enter if (keybParam.typing.length() > 0) longActPress = 1; } else if ((keyState & GUI_PAD_ACT) && (keybParam.selX == 18 && keybParam.selY == 2)) { // back space if (keybParam.typing.length() > 0) keybParam.typing.remove(keybParam.typing.length() - 1); } else if ((keyState & GUI_PAD_ACT) && (keybParam.selX == 17 && keybParam.selY == 1)) { // SPACE if (keybParam.typing.length() < GUI_MAX_TYPING_CHARS) keybParam.typing += " "; } else if ((keyState & GUI_PAD_ACT) && (keybParam.selX == 17 && keybParam.selY == 2)) { keybParam.shiftOn = !keybParam.shiftOn; drawKeyboard(keybParam.selX, keybParam.selY, 0); waitKeyUnpressed(); } else if (keyState & GUI_PAD_ACT){ if (waitKeyUnpressed() > GUI_KEY_PRESSED_DELAY_TO_SEND) longActPress = 1; else if (keybParam.typing.length() < GUI_MAX_TYPING_CHARS) keybParam.typing += (char)pgm_read_byte(&keybOnscr[keybParam.shiftOn][keybParam.selY][keybParam.selX]); } if (keyState & GUI_PAD_ESC) { if (waitKeyUnpressed() > GUI_KEY_PRESSED_DELAY_TO_SEND) keybParam.typing = ""; else if (keybParam.typing.length() > 0) keybParam.typing.remove(keybParam.typing.length() - 1); } } else { if ((keyState & GUI_PAD_ACT && keyState & GUI_PAD_ESC) || (keyState & GUI_PAD_RGT && keyState & GUI_PAD_LFT)) { toggleDisplayMode(0); waitKeyUnpressed(); } else if (((keyState & GUI_PAD_RGT || keyState & GUI_PAD_RIGHT || keyState & GUI_PAD_DOWN)) && keybParam.renderLine > 0) { keybParam.renderLine--; drawConsole(0); } else if (((keyState&GUI_PAD_LFT || keyState & GUI_PAD_LEFT || keyState&GUI_PAD_UP)) && keybParam.renderLine < consoleStringsVector.size() - GUI_MAX_STRINGS_ONSCREEN_FULL) { keybParam.renderLine++; drawConsole(0); } else if (keyState & GUI_PAD_ESC) toggleDisplayMode(0); } if (!keybParam.displayMode) drawKeyboard(keybParam.selX, keybParam.selY, 1); } if (!keybParam.displayMode) drawBlinkingCursor(); return (longActPress); } void ESPboyTerminalGUI::toggleDisplayMode(uint8_t mode) { keybParam.displayMode = mode; tft->fillScreen(TFT_BLACK); tft->drawRect(0, 0, 128, 128, TFT_NAVY); if (!keybParam.displayMode) { tft->drawRect(0, 128 - 3 * 8 - 5, 128, 3 * 8 + 5, TFT_NAVY); tft->drawRect(0, 0, 128, 86, TFT_NAVY); } if (!keybParam.displayMode) { drawKeyboard(keybParam.selX, keybParam.selY, 0); } drawConsole(0); } String ESPboyTerminalGUI::getUserInput() { String userInput; toggleDisplayMode(0); while (1) { while (!keysAction()) delay(GUI_KEYB_CALL_DELAY); if (keybParam.typing != "") break; } toggleDisplayMode(1); userInput = keybParam.typing; keybParam.typing = ""; return (userInput); } void ESPboyTerminalGUI::doScroll(){ uint8_t keyState=0; #ifdef buttonclicks tone(SOUNDPIN, 100, 10); #endif toggleDisplayMode(1); while (!(keyState & GUI_PAD_ESC)){ delay(1); keyState = getKeys(); if(keyState){ if (keyState&GUI_PAD_RGT && keybParam.renderLine) { keybParam.renderLine--;} if (keyState&GUI_PAD_LFT && keybParam.renderLine < consoleStringsVector.size() - GUI_MAX_STRINGS_ONSCREEN_SMALL) { keybParam.renderLine++;} drawConsole(0); #ifdef buttonclicks tone(SOUNDPIN, 100, 10); #endif delay(GUI_KEYB_CALL_DELAY); } } } void ESPboyTerminalGUI::printConsole(String bfrstr, uint16_t color, uint8_t ln, uint8_t noAddLine) { String toprint; keybParam.renderLine = 0; if(bfrstr == "") bfrstr = " "; if (!ln) if (bfrstr.length() > ((128-4)/GUI_FONT_WIDTH)) { bfrstr = bfrstr.substring(0, ((128-4)/GUI_FONT_WIDTH)); toprint = bfrstr; } for (uint8_t i = 0; i <= ((bfrstr.length()-1) / ((128-4)/GUI_FONT_WIDTH)); i++) { toprint = bfrstr.substring(i * (128-4)/GUI_FONT_WIDTH); toprint = toprint.substring(0, (128-4)/GUI_FONT_WIDTH); if (!noAddLine) consoleStringsVector.push_back(consoleStringS()); consoleStringsVector.back().consoleString = toprint; consoleStringsVector.back().consoleStringColor = color; } drawConsole(noAddLine); if (consoleStringsVector.size() > GUI_MAX_CONSOLE_STRINGS){ consoleStringsVector.erase(consoleStringsVector.begin()); consoleStringsVector.shrink_to_fit(); } } void ESPboyTerminalGUI::drawConsole(uint8_t onlyLastLine) { uint16_t lines; uint16_t offsetY; uint16_t quantityLinesToDraw; int16_t startVectorToDraw; if (keybParam.displayMode) lines = GUI_MAX_STRINGS_ONSCREEN_FULL; else lines = GUI_MAX_STRINGS_ONSCREEN_SMALL; #ifndef U8g2 if (!onlyLastLine) tft->fillRect(1, 4, 126, lines * GUI_FONT_HEIGHT, TFT_BLACK); else tft->fillRect(1, (lines-1) * GUI_FONT_HEIGHT+4, 126, GUI_FONT_HEIGHT, TFT_BLACK); #else if (!onlyLastLine) tft->fillRect(1, 1, 126, lines * GUI_FONT_HEIGHT, TFT_BLACK); else tft->fillRect(1, (lines-1) * GUI_FONT_HEIGHT, 126, GUI_FONT_HEIGHT+1, TFT_BLACK); #endif #ifndef U8g2 offsetY = lines * GUI_FONT_HEIGHT - 4; #else offsetY = lines * GUI_FONT_HEIGHT; #endif if(consoleStringsVector.size() < lines ) quantityLinesToDraw = consoleStringsVector.size(); else quantityLinesToDraw = lines; startVectorToDraw = consoleStringsVector.size()-1-keybParam.renderLine; for (uint8_t i = 0; i< quantityLinesToDraw; i++) { #ifndef U8g2 tft->setTextColor(consoleStringsVector[startVectorToDraw].consoleStringColor, TFT_BLACK); tft->drawString(consoleStringsVector[startVectorToDraw].consoleString, 3, offsetY); #else u8f->setForegroundColor(consoleStringsVector[startVectorToDraw].consoleStringColor); u8f->drawStr(2, offsetY, consoleStringsVector[startVectorToDraw].consoleString.c_str()); #endif offsetY -= GUI_FONT_HEIGHT; startVectorToDraw--; if(startVectorToDraw<0) startVectorToDraw=0; } } uint8_t ESPboyTerminalGUI::getKeys() { return (~mcp->readGPIOAB() & 255); } uint32_t ESPboyTerminalGUI::waitKeyUnpressed() { uint32_t timerStamp = millis(); while (getKeys() && (millis() - timerStamp) < GUI_KEY_UNPRESSED_TIMEOUT) delay(1); return (millis() - timerStamp); } void ESPboyTerminalGUI::drawKeyboard(uint8_t slX, uint8_t slY, uint8_t onlySelected) { static char chr[2]={0,0}; static uint8_t prevX = 0, prevY = 0; if (!onlySelected) { tft->fillRect(1, 128 - 24, 126, 23, TFT_BLACK); tft->setTextColor(TFT_YELLOW, TFT_BLACK); for (uint8_t j = 0; j < 3; j++) for (uint8_t i = 0; i < 20; i++) { chr[0] = pgm_read_byte(&keybOnscr[keybParam.shiftOn][j][i]); tft->drawString(&chr[0], i * 6 + 4, 128 - 2 - 8 * (3 - j)); } } tft->setTextColor(TFT_YELLOW, TFT_BLACK); chr[0] = pgm_read_byte(&keybOnscr[keybParam.shiftOn][prevY][prevX]); tft->drawString(&chr[0], prevX * 6 + 4, 128 - 24 + prevY * 8 - 2); tft->setTextColor(TFT_WHITE, TFT_BLACK); tft->drawString("^setTextColor(TFT_YELLOW, TFT_RED); chr[0] = pgm_read_byte(&keybOnscr[keybParam.shiftOn][slY][slX]); tft->drawString(&chr[0], slX * 6 + 4, 128 - 24 + slY * 8 - 2); prevX = slX; prevY = slY; drawTyping(0); } void ESPboyTerminalGUI::drawTyping(uint8_t changeCursor) { static char cursorType[2] = {220, '_'}; static uint8_t cursorTypeFlag=0; if(changeCursor) cursorTypeFlag=!cursorTypeFlag; tft->fillRect(1, 128 - 5 * 8, 126, 10, TFT_BLACK); if (keybParam.typing.length() < 20) { tft->setTextColor(TFT_WHITE, TFT_BLACK); tft->drawString(keybParam.typing + cursorType[cursorTypeFlag], 4, 128 - 5 * 8 + 1); } else { tft->setTextColor(TFT_WHITE, TFT_BLACK); tft->drawString("<" + keybParam.typing.substring(keybParam.typing.length() - 18) +cursorType[cursorTypeFlag], 4, 128 - 5 * 8 + 1); } } void ESPboyTerminalGUI::drawOwnTypingLine(String typingLine, uint16_t colorLine){ keybParam.typing = typingLine; toggleDisplayMode(0); keybParam.typing = typingLine; tft->fillRect(1, 128 - 5 * 8, 126, 10, TFT_BLACK); tft->setTextColor(colorLine, TFT_BLACK); tft->drawString(keybParam.typing, 4, 128 - 5 * 8 + 1); } void ESPboyTerminalGUI::drawBlinkingCursor() { static uint32_t cursorBlinkMillis = 0; if (millis() > (cursorBlinkMillis + GUI_CURSOR_BLINKING_PERIOD)) { cursorBlinkMillis = millis(); drawTyping(1); } } void ESPboyTerminalGUI::SetKeybParamTyping(String str){ keybParam.typing = str; }