From ab342ce904d73aa2f66a0f8fbef615b815d0c998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 20 Sep 2022 13:50:18 +0200 Subject: [PATCH 1/6] Freetext Input with CardKB Take 1 - Also removes FacesKB support, this thing is ancient. --- src/configuration.h | 3 -- src/detect/i2cScan.h | 4 -- src/input/InputBroker.h | 3 ++ src/input/cardKbI2cImpl.cpp | 1 - src/input/facesKbI2cImpl.cpp | 20 -------- src/input/facesKbI2cImpl.h | 20 -------- src/input/kbI2cBase.cpp | 13 ++++- src/main.cpp | 3 -- src/main.h | 1 - src/modules/CannedMessageModule.cpp | 74 +++++++++++++++++++++++++---- src/modules/CannedMessageModule.h | 3 ++ src/modules/Modules.cpp | 3 -- 12 files changed, 83 insertions(+), 65 deletions(-) delete mode 100644 src/input/facesKbI2cImpl.cpp delete mode 100644 src/input/facesKbI2cImpl.h diff --git a/src/configuration.h b/src/configuration.h index b42a76940..f7d42afcd 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -104,9 +104,6 @@ along with this program. If not, see . // The m5stack I2C Keyboard (also RAK14004) #define CARDKB_ADDR 0x5F -// The older M5 Faces I2C Keyboard -#define FACESKB_ADDR 0x88 - // ----------------------------------------------------------------------------- // SENSOR // ----------------------------------------------------------------------------- diff --git a/src/detect/i2cScan.h b/src/detect/i2cScan.h index f5f8effc9..3591d307b 100644 --- a/src/detect/i2cScan.h +++ b/src/detect/i2cScan.h @@ -107,10 +107,6 @@ void scanI2Cdevice(void) kb_model = 0x00; } } - if (addr == FACESKB_ADDR) { - faceskb_found = addr; - DEBUG_MSG("m5 Faces found\n"); - } if (addr == ST7567_ADDRESS) { screen_found = addr; DEBUG_MSG("st7567 display found\n"); diff --git a/src/input/InputBroker.h b/src/input/InputBroker.h index e75b0c407..5736a8a65 100644 --- a/src/input/InputBroker.h +++ b/src/input/InputBroker.h @@ -1,9 +1,12 @@ #pragma once #include "Observer.h" +#define ANYKEY 0xFF + typedef struct _InputEvent { const char* source; char inputEvent; + char kbchar; } InputEvent; class InputBroker : public Observable diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp index 1a101aee7..de0fbbd38 100644 --- a/src/input/cardKbI2cImpl.cpp +++ b/src/input/cardKbI2cImpl.cpp @@ -16,6 +16,5 @@ void CardKbI2cImpl::init() return; } - DEBUG_MSG("registerSource\n"); inputBroker->registerSource(this); } diff --git a/src/input/facesKbI2cImpl.cpp b/src/input/facesKbI2cImpl.cpp deleted file mode 100644 index c91350763..000000000 --- a/src/input/facesKbI2cImpl.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "facesKbI2cImpl.h" -#include "InputBroker.h" - -FacesKbI2cImpl *facesKbI2cImpl; - -FacesKbI2cImpl::FacesKbI2cImpl() : - KbI2cBase("facesKB") -{ -} - -void FacesKbI2cImpl::init() -{ - if (faceskb_found != FACESKB_ADDR) - { - // Input device is not detected. - return; - } - - inputBroker->registerSource(this); -} diff --git a/src/input/facesKbI2cImpl.h b/src/input/facesKbI2cImpl.h deleted file mode 100644 index edf73bd05..000000000 --- a/src/input/facesKbI2cImpl.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "kbI2cBase.h" -#include "main.h" - -/** - * @brief The idea behind this class to have static methods for the event handlers. - * Check attachInterrupt() at RotaryEncoderInteruptBase.cpp - * Technically you can have as many rotary encoders hardver attached - * to your device as you wish, but you always need to have separate event - * handlers, thus you need to have a RotaryEncoderInterrupt implementation. - */ -class FacesKbI2cImpl : - public KbI2cBase -{ - public: - FacesKbI2cImpl(); - void init(); -}; - -extern FacesKbI2cImpl *facesKbI2cImpl; \ No newline at end of file diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp index e12b52e5b..9ff7d7e10 100644 --- a/src/input/kbI2cBase.cpp +++ b/src/input/kbI2cBase.cpp @@ -3,7 +3,6 @@ #include extern uint8_t cardkb_found; -extern uint8_t faceskb_found; KbI2cBase::KbI2cBase(const char *name) : concurrency::OSThread(name) { @@ -12,7 +11,7 @@ KbI2cBase::KbI2cBase(const char *name) : concurrency::OSThread(name) int32_t KbI2cBase::runOnce() { - if ((cardkb_found != CARDKB_ADDR) && (faceskb_found != CARDKB_ADDR)){ + if (cardkb_found != CARDKB_ADDR){ // Input device is not detected. return INT32_MAX; } @@ -30,6 +29,7 @@ int32_t KbI2cBase::runOnce() break; case 0x08: // Back e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_BACK; + e.kbchar = c; break; case 0xb5: // Up e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_UP; @@ -39,13 +39,22 @@ int32_t KbI2cBase::runOnce() break; case 0xb4: // Left e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_LEFT; + e.kbchar = c; break; case 0xb7: // Right e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT; + e.kbchar = c; break; case 0x0d: // Enter e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; break; + case 0x00: //nopress + e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + break; + default: // all other keys + e.inputEvent = ANYKEY; + e.kbchar = c; + break; } } diff --git a/src/main.cpp b/src/main.cpp index 260e9aec3..c5e6e8b56 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,9 +74,6 @@ uint8_t cardkb_found; // 0x02 for RAK14004 and 0x00 for cardkb uint8_t kb_model; -// The I2C address of the Faces Keyboard (if found) -uint8_t faceskb_found; - // The I2C address of the RTC Module (if found) uint8_t rtc_found; diff --git a/src/main.h b/src/main.h index ed48a83cd..e1142d36c 100644 --- a/src/main.h +++ b/src/main.h @@ -11,7 +11,6 @@ extern uint8_t screen_found; extern uint8_t screen_model; extern uint8_t cardkb_found; extern uint8_t kb_model; -extern uint8_t faceskb_found; extern uint8_t rtc_found; extern bool eink_found; diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 54c02f67f..2ffe221dc 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -116,10 +116,40 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) if ((this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED)) { powerFSM.trigger(EVENT_PRESS); } else { + this->payload = this->runState; this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT; validEvent = true; } } + if (event->inputEvent == static_cast(ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) { + DEBUG_MSG("Canned message event Cancel\n"); + // emulate a timeout. Same result + this->lastTouchMillis = 0; + validEvent = true; + } + if ((event->inputEvent == static_cast(ModuleConfig_CannedMessageConfig_InputEventChar_BACK)) || + (event->inputEvent == static_cast(ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) || + (event->inputEvent == static_cast(ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))) { + DEBUG_MSG("Canned message event (back/left/right)\n"); + if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { + // pass the pressed key + this->payload = event->kbchar; + this->lastTouchMillis = millis(); + validEvent = true; + } + } + if (event->inputEvent == static_cast(ANYKEY)) { + DEBUG_MSG("Canned message event any key pressed\n"); + // when inactive, this will switch to the freetext mode + if ((this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED)) { + this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT; + } + // pass the pressed key + this->payload = event->kbchar; + this->lastTouchMillis = millis(); + validEvent = true; + } + if (validEvent) { // Let runOnce to be called immediately. @@ -160,25 +190,32 @@ int32_t CannedMessageModule::runOnce() this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; e.frameChanged = true; this->currentMessageIndex = -1; + this->freetext = ""; // clear freetext this->notifyObservers(&e); - } else if ((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) && (millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS) { + } else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) && (millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS) { // Reset module DEBUG_MSG("Reset due the lack of activity.\n"); e.frameChanged = true; this->currentMessageIndex = -1; + this->freetext = ""; // clear freetext this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; this->notifyObservers(&e); - } else if (this->currentMessageIndex == -1) { + } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) { + if (this->payload == CANNED_MESSAGE_RUN_STATE_FREETEXT) { + sendText(NODENUM_BROADCAST, this->freetext.c_str(), true); + } else { + sendText(NODENUM_BROADCAST, this->messages[this->currentMessageIndex], true); + } + this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE; + this->currentMessageIndex = -1; + this->freetext = ""; // clear freetext + this->notifyObservers(&e); + return 2000; + } else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) { this->currentMessageIndex = 0; DEBUG_MSG("First touch (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); e.frameChanged = true; this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; - } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) { - sendText(NODENUM_BROADCAST, this->messages[this->currentMessageIndex], true); - this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE; - this->currentMessageIndex = -1; - this->notifyObservers(&e); - return 2000; } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) { this->currentMessageIndex = getPrevIndex(); this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; @@ -187,6 +224,22 @@ int32_t CannedMessageModule::runOnce() this->currentMessageIndex = this->getNextIndex(); this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; DEBUG_MSG("MOVE DOWN (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); + } else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { + e.frameChanged = true; + switch (this->payload) { + case 8: // backspace + if (this->freetext.length() > 0) { + this->freetext = this->freetext.substring(0, this->freetext.length() - 1); + } + break; + default: + this->freetext += this->payload; + break; + } + + this->lastTouchMillis = millis(); + this->notifyObservers(&e); + return INACTIVATE_AFTER_MS; } if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) { @@ -248,6 +301,11 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); display->drawString(10 + x, 0 + y + 16, "Canned Message\nModule disabled."); + }else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_MEDIUM); + display->drawString(0 + x, 0 + y, "To: Broadcast"); + display->drawString(0 + x, 0 + y + 16, this->freetext); } else { display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index 2bd2927e1..e28b67902 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -8,6 +8,7 @@ enum cannedMessageModuleRunState CANNED_MESSAGE_RUN_STATE_DISABLED, CANNED_MESSAGE_RUN_STATE_INACTIVE, CANNED_MESSAGE_RUN_STATE_ACTIVE, + CANNED_MESSAGE_RUN_STATE_FREETEXT, CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE, CANNED_MESSAGE_RUN_STATE_ACTION_SELECT, CANNED_MESSAGE_RUN_STATE_ACTION_UP, @@ -70,6 +71,8 @@ class CannedMessageModule : int currentMessageIndex = -1; cannedMessageModuleRunState runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; + char payload; + String freetext; // Text Buffer for Freetext Editor char messageStore[CANNED_MESSAGE_MODULE_MESSAGES_SIZE+1]; char *messages[CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT]; diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 7ff90d9ca..91f3f2edd 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -3,7 +3,6 @@ #include "input/RotaryEncoderInterruptImpl1.h" #include "input/UpDownInterruptImpl1.h" #include "input/cardKbI2cImpl.h" -#include "input/facesKbI2cImpl.h" #include "modules/AdminModule.h" #include "modules/CannedMessageModule.h" #include "modules/NodeInfoModule.h" @@ -53,8 +52,6 @@ void setupModules() upDownInterruptImpl1->init(); cardKbI2cImpl = new CardKbI2cImpl(); cardKbI2cImpl->init(); - facesKbI2cImpl = new FacesKbI2cImpl(); - facesKbI2cImpl->init(); #endif #if HAS_SCREEN cannedMessageModule = new CannedMessageModule(); From d0ad5dd4cf22b3c0a559cfc6cc3cef12a528c905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 20 Sep 2022 14:28:42 +0200 Subject: [PATCH 2/6] - don't swallow keystrokes - switch back and forth between the 2 modes. --- src/input/kbI2cBase.cpp | 12 ++++++------ src/modules/CannedMessageModule.cpp | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp index 9ff7d7e10..14e17b7a2 100644 --- a/src/input/kbI2cBase.cpp +++ b/src/input/kbI2cBase.cpp @@ -15,14 +15,14 @@ int32_t KbI2cBase::runOnce() // Input device is not detected. return INT32_MAX; } - InputEvent e; - e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; - e.source = this->_originName; Wire.requestFrom(CARDKB_ADDR, 1); while (Wire.available()) { char c = Wire.read(); + InputEvent e; + e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + e.source = this->_originName; switch (c) { case 0x1b: // ESC e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL; @@ -56,10 +56,10 @@ int32_t KbI2cBase::runOnce() e.kbchar = c; break; } - } - if (e.inputEvent != ModuleConfig_CannedMessageConfig_InputEventChar_NONE) { - this->notifyObservers(&e); + if (e.inputEvent != ModuleConfig_CannedMessageConfig_InputEventChar_NONE) { + this->notifyObservers(&e); + } } return 500; } diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 2ffe221dc..dc118dc5d 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -141,7 +141,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) if (event->inputEvent == static_cast(ANYKEY)) { DEBUG_MSG("Canned message event any key pressed\n"); // when inactive, this will switch to the freetext mode - if ((this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED)) { + if ((this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED)) { this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT; } // pass the pressed key @@ -218,10 +218,12 @@ int32_t CannedMessageModule::runOnce() this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) { this->currentMessageIndex = getPrevIndex(); + this->freetext = ""; // clear freetext this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; DEBUG_MSG("MOVE UP (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_DOWN) { this->currentMessageIndex = this->getNextIndex(); + this->freetext = ""; // clear freetext this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; DEBUG_MSG("MOVE DOWN (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); } else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { From 0a8293a2d6839299450554a1a9ea83b0c29f989a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 20 Sep 2022 16:21:32 +0200 Subject: [PATCH 3/6] Cursor editing --- src/graphics/Screen.cpp | 1 + src/modules/CannedMessageModule.cpp | 70 ++++++++++++++++++++++++++--- src/modules/CannedMessageModule.h | 5 ++- 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 77f61a796..6e195f133 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -117,6 +117,7 @@ static uint16_t displayWidth, displayHeight; #define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL) #define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM) +#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE) #define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2) diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index dc118dc5d..bf6c166d1 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -6,10 +6,26 @@ #include "PowerFSM.h" // neede for button bypass #include "mesh/generated/cannedmessages.pb.h" -// TODO: reuse defined from Screen.cpp +#if defined(USE_EINK) || defined(ILI9341_DRIVER) +// The screen is bigger so use bigger fonts +#define FONT_SMALL ArialMT_Plain_16 +#define FONT_MEDIUM ArialMT_Plain_24 +#define FONT_LARGE ArialMT_Plain_24 +#else +#ifdef OLED_RU +#define FONT_SMALL ArialMT_Plain_10_RU +#else #define FONT_SMALL ArialMT_Plain_10 +#endif #define FONT_MEDIUM ArialMT_Plain_16 #define FONT_LARGE ArialMT_Plain_24 +#endif + +#define fontHeight(font) ((font)[1] + 1) // height is position 1 + +#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL) +#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM) +#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE) // Remove Canned message screen if no action is taken for some milliseconds #define INACTIVATE_AFTER_MS 20000 @@ -130,7 +146,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) if ((event->inputEvent == static_cast(ModuleConfig_CannedMessageConfig_InputEventChar_BACK)) || (event->inputEvent == static_cast(ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) || (event->inputEvent == static_cast(ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))) { - DEBUG_MSG("Canned message event (back/left/right)\n"); + DEBUG_MSG("Canned message event (%x)\n",event->kbchar); if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { // pass the pressed key this->payload = event->kbchar; @@ -191,6 +207,7 @@ int32_t CannedMessageModule::runOnce() e.frameChanged = true; this->currentMessageIndex = -1; this->freetext = ""; // clear freetext + this->cursor = 0; this->notifyObservers(&e); } else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) && (millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS) { // Reset module @@ -198,6 +215,7 @@ int32_t CannedMessageModule::runOnce() e.frameChanged = true; this->currentMessageIndex = -1; this->freetext = ""; // clear freetext + this->cursor = 0; this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; this->notifyObservers(&e); } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) { @@ -209,6 +227,7 @@ int32_t CannedMessageModule::runOnce() this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE; this->currentMessageIndex = -1; this->freetext = ""; // clear freetext + this->cursor = 0; this->notifyObservers(&e); return 2000; } else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) { @@ -219,23 +238,48 @@ int32_t CannedMessageModule::runOnce() } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) { this->currentMessageIndex = getPrevIndex(); this->freetext = ""; // clear freetext + this->cursor = 0; this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; DEBUG_MSG("MOVE UP (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_DOWN) { this->currentMessageIndex = this->getNextIndex(); this->freetext = ""; // clear freetext + this->cursor = 0; this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; DEBUG_MSG("MOVE DOWN (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); } else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { + DEBUG_MSG("KEYSTROKE (%d):%x\n", this->cursor, this->payload); e.frameChanged = true; switch (this->payload) { + case 0xb4: // left + if (this->cursor > 0) { + this->cursor--; + } + break; + case 0xb7: // right + if (this->cursor < this->freetext.length()) { + this->cursor++; + } + break; case 8: // backspace if (this->freetext.length() > 0) { this->freetext = this->freetext.substring(0, this->freetext.length() - 1); + this->cursor--; } break; default: - this->freetext += this->payload; + if(this->cursor == this->freetext.length()) { + this->freetext += this->payload; + } else { + this->freetext = this->freetext.substring(0, this->cursor) + + this->payload + + this->freetext.substring(this->cursor); + } + this->cursor += 1; + if (this->freetext.length() > Constants_DATA_PAYLOAD_LEN) { + this->cursor = Constants_DATA_PAYLOAD_LEN; + this->freetext = this->freetext.substring(0, Constants_DATA_PAYLOAD_LEN); + } break; } @@ -302,20 +346,24 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_DISABLED) { display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); - display->drawString(10 + x, 0 + y + 16, "Canned Message\nModule disabled."); + display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled."); }else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_MEDIUM); display->drawString(0 + x, 0 + y, "To: Broadcast"); - display->drawString(0 + x, 0 + y + 16, this->freetext); + // used chars right aligned + char buffer[9]; + sprintf(buffer, "%d left", Constants_DATA_PAYLOAD_LEN - this->freetext.length()); + display->drawString(x + display->getWidth() - display->getStringWidth(buffer), y + 0, buffer); + display->drawString(0 + x, 0 + y + FONT_HEIGHT_MEDIUM, cannedMessageModule->drawWithCursor(cannedMessageModule->freetext, cannedMessageModule->cursor)); } else { display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); display->drawString(0 + x, 0 + y, cannedMessageModule->getPrevMessage()); display->setFont(FONT_MEDIUM); - display->drawString(0 + x, 0 + y + 8, cannedMessageModule->getCurrentMessage()); + display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL, cannedMessageModule->getCurrentMessage()); display->setFont(FONT_SMALL); - display->drawString(0 + x, 0 + y + 24, cannedMessageModule->getNextMessage()); + display->drawString(0 + x, 0 + y + FONT_HEIGHT_MEDIUM, cannedMessageModule->getNextMessage()); } } @@ -414,4 +462,12 @@ void CannedMessageModule::handleSetCannedMessageModuleMessages(const char *from_ } } +String CannedMessageModule::drawWithCursor(String text, int cursor) +{ + String result = text.substring(0, cursor) + + "_" + + text.substring(cursor); + return result; +} + #endif \ No newline at end of file diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index e28b67902..33f60dd61 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -43,6 +43,8 @@ class CannedMessageModule : void handleGetCannedMessageModuleMessages(const MeshPacket &req, AdminMessage *response); void handleSetCannedMessageModuleMessages(const char *from_msg); + String drawWithCursor(String text, int cursor); + protected: virtual int32_t runOnce() override; @@ -72,7 +74,8 @@ class CannedMessageModule : int currentMessageIndex = -1; cannedMessageModuleRunState runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; char payload; - String freetext; // Text Buffer for Freetext Editor + int cursor = 0; + String freetext = ""; // Text Buffer for Freetext Editor char messageStore[CANNED_MESSAGE_MODULE_MESSAGES_SIZE+1]; char *messages[CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT]; From 6de77ee3102efc2456e42a3c230637d45d1fe8a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 20 Sep 2022 16:30:21 +0200 Subject: [PATCH 4/6] use cyrillic font if defined --- src/modules/CannedMessageModule.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index bf6c166d1..f30ef7767 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -6,6 +6,10 @@ #include "PowerFSM.h" // neede for button bypass #include "mesh/generated/cannedmessages.pb.h" +#ifdef OLED_RU +#include "fonts/OLEDDisplayFontsRU.h" +#endif + #if defined(USE_EINK) || defined(ILI9341_DRIVER) // The screen is bigger so use bigger fonts #define FONT_SMALL ArialMT_Plain_16 @@ -248,7 +252,6 @@ int32_t CannedMessageModule::runOnce() this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; DEBUG_MSG("MOVE DOWN (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); } else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { - DEBUG_MSG("KEYSTROKE (%d):%x\n", this->cursor, this->payload); e.frameChanged = true; switch (this->payload) { case 0xb4: // left From b73e240f4d17bb6e5ba2d741427724fc6402456c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 20 Sep 2022 16:36:50 +0200 Subject: [PATCH 5/6] relative paths are relative --- src/modules/CannedMessageModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index f30ef7767..22af8aff9 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -7,7 +7,7 @@ #include "mesh/generated/cannedmessages.pb.h" #ifdef OLED_RU -#include "fonts/OLEDDisplayFontsRU.h" +#include "graphics/fonts/OLEDDisplayFontsRU.h" #endif #if defined(USE_EINK) || defined(ILI9341_DRIVER) From 011db443ba0c6b8c9feb4504fde26bbbe75a956d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 21 Sep 2022 10:42:10 +0200 Subject: [PATCH 6/6] Bugfixes in Freetext Module. - work without fixed messages defined - honour cursor position on backspace - don't send an empty string - compiler warnings in font engine fixed --- src/graphics/Screen.h | 44 +++++++++++++++-------------- src/modules/CannedMessageModule.cpp | 31 ++++++++++++++++---- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 2fa702cec..e38f6de66 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -213,27 +213,29 @@ class Screen : public concurrency::OSThread LASTCHAR = ch; switch (last) { // conversion depending on first UTF8-character - case 0xC2: { - SKIPREST = false; - return (uint8_t)ch; - } - case 0xC3: { - SKIPREST = false; - return (uint8_t)(ch | 0xC0); - } - // map UTF-8 cyrillic chars to it Windows-1251 (CP-1251) ASCII codes - // note: in this case we must use compatible font - provided ArialMT_Plain_10/16/24 by 'ThingPulse/esp8266-oled-ssd1306' library - // have empty chars for non-latin ASCII symbols - case 0xD0: { - SKIPREST = false; - if (ch == 129) return (uint8_t)(168); // Ё - if (ch > 143 && ch < 192) return (uint8_t)(ch + 48); - } - case 0xD1: { - SKIPREST = false; - if (ch == 145) return (uint8_t)(184); // ё - if (ch > 127 && ch < 144) return (uint8_t)(ch + 112); - } + case 0xC2: { + SKIPREST = false; + return (uint8_t)ch; + } + case 0xC3: { + SKIPREST = false; + return (uint8_t)(ch | 0xC0); + } + // map UTF-8 cyrillic chars to it Windows-1251 (CP-1251) ASCII codes + // note: in this case we must use compatible font - provided ArialMT_Plain_10/16/24 by 'ThingPulse/esp8266-oled-ssd1306' library + // have empty chars for non-latin ASCII symbols + case 0xD0: { + SKIPREST = false; + if (ch == 129) return (uint8_t)(168); // Ё + if (ch > 143 && ch < 192) return (uint8_t)(ch + 48); + break; + } + case 0xD1: { + SKIPREST = false; + if (ch == 145) return (uint8_t)(184); // ё + if (ch > 127 && ch < 144) return (uint8_t)(ch + 112); + break; + } } // We want to strip out prefix chars for two-byte char formats diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 22af8aff9..240c23086 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -34,6 +34,8 @@ // Remove Canned message screen if no action is taken for some milliseconds #define INACTIVATE_AFTER_MS 20000 +extern uint8_t cardkb_found; + static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto"; CannedMessageModuleConfig cannedMessageModuleConfig; @@ -50,7 +52,7 @@ CannedMessageModule::CannedMessageModule() { if (moduleConfig.canned_message.enabled) { this->loadProtoForModule(); - if (this->splitConfiguredMessages() <= 0) { + if ((this->splitConfiguredMessages() <= 0) && (cardkb_found != CARDKB_ADDR)) { DEBUG_MSG("CannedMessageModule: No messages are configured. Module is disabled\n"); this->runState = CANNED_MESSAGE_RUN_STATE_DISABLED; } else { @@ -215,7 +217,7 @@ int32_t CannedMessageModule::runOnce() this->notifyObservers(&e); } else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) && (millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS) { // Reset module - DEBUG_MSG("Reset due the lack of activity.\n"); + DEBUG_MSG("Reset due to lack of activity.\n"); e.frameChanged = true; this->currentMessageIndex = -1; this->freetext = ""; // clear freetext @@ -224,11 +226,24 @@ int32_t CannedMessageModule::runOnce() this->notifyObservers(&e); } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) { if (this->payload == CANNED_MESSAGE_RUN_STATE_FREETEXT) { - sendText(NODENUM_BROADCAST, this->freetext.c_str(), true); + if (this->freetext.length() > 0) { + sendText(NODENUM_BROADCAST, this->freetext.c_str(), true); + this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE; + } else { + DEBUG_MSG("Reset message is empty.\n"); + this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; + e.frameChanged = true; + } } else { - sendText(NODENUM_BROADCAST, this->messages[this->currentMessageIndex], true); + if (strlen(this->messages[this->currentMessageIndex]) > 0) { + sendText(NODENUM_BROADCAST, this->messages[this->currentMessageIndex], true); + this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE; + } else { + DEBUG_MSG("Reset message is empty.\n"); + this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; + e.frameChanged = true; + } } - this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE; this->currentMessageIndex = -1; this->freetext = ""; // clear freetext this->cursor = 0; @@ -266,7 +281,11 @@ int32_t CannedMessageModule::runOnce() break; case 8: // backspace if (this->freetext.length() > 0) { - this->freetext = this->freetext.substring(0, this->freetext.length() - 1); + if(this->cursor == this->freetext.length()) { + this->freetext = this->freetext.substring(0, this->freetext.length() - 1); + } else { + this->freetext = this->freetext.substring(0, this->cursor - 1) + this->freetext.substring(this->cursor, this->freetext.length()); + } this->cursor--; } break;