diff --git a/PCB/Moonshine_PCB.eprj2 b/PCB/Moonshine_PCB.eprj2 index cef4cf2..a7fb111 100644 Binary files a/PCB/Moonshine_PCB.eprj2 and b/PCB/Moonshine_PCB.eprj2 differ diff --git a/PCB/Moonshine_travelers.eprj2 b/PCB/Moonshine_travelers.eprj2 index c007c0d..90275dd 100644 Binary files a/PCB/Moonshine_travelers.eprj2 and b/PCB/Moonshine_travelers.eprj2 differ diff --git a/code/firmware-2.7.15.567b8ea/.workbuddy/memory/2026-05-03.md b/code/firmware-2.7.15.567b8ea/.workbuddy/memory/2026-05-03.md new file mode 100644 index 0000000..fb32f73 --- /dev/null +++ b/code/firmware-2.7.15.567b8ea/.workbuddy/memory/2026-05-03.md @@ -0,0 +1,30 @@ + +## 2026-05-03 工作日志 + +### 添加中文输入法到 CannedMessageModule + +**修改的文件:** +- `src/modules/CannedMessageModule.h` - 添加 InputMode::CHINESE 枚举值,添加中文输入相关成员变量和方法声明 +- `src/modules/CannedMessageModule.cpp` - 实现中文输入法逻辑 + +**实现的功能:** +1. 在 `InputMode` 枚举中添加 `CHINESE`,支持4种输入模式切换(DIGIT/LOWER/UPPER/CHINESE) +2. 修改 `#` 键切换逻辑:`(m + 1) % 4` 支持4种模式 +3. 在 `handleFreeTextInput()` 中添加中文模式检查,调用 `handleChineseInput()` +4. 实现 `handleChineseInput()` 方法: + - 数字键1-6选择常用汉字(我/你/他/是/的/好) + - `*` 键删除最后一个字符(正确处理UTF-8中文编码) + - SELECT键切换回字母输入模式 +5. 实现 `drawChineseInput()` 方法:绘制中文输入界面 + +**使用方式:** +- 在文本输入界面按 `#` 键切换到中文模式 +- 按数字键1-6选择汉字 +- 按 `*` 键删除字符 +- 按 SELECT 键退出中文模式 + +**限制:** +- 当前只支持6个常用汉字 +- 未实现拼音输入法(需要更大的字典) +- 后续可扩展为支持翻页(按"0"键)和更多汉字 + diff --git a/code/firmware-2.7.15.567b8ea/src/modules/CannedMessageModule.cpp b/code/firmware-2.7.15.567b8ea/src/modules/CannedMessageModule.cpp index 6e5b8ee..3ffb562 100644 --- a/code/firmware-2.7.15.567b8ea/src/modules/CannedMessageModule.cpp +++ b/code/firmware-2.7.15.567b8ea/src/modules/CannedMessageModule.cpp @@ -57,6 +57,12 @@ const char *const CannedMessageModule::t9LetterMap[][5] = { #include "graphics/fonts/ChineseFont12x12.h" #include +// === 中文输入法:常用汉字列表(6个)=== +// 在 handleChineseInput() 和 drawChineseInput() 方法中硬编码使用 +// 格式:数字键 -> 汉字 +static const char* chineseChars[] = {"我", "你", "他", "是", "的", "好"}; + + // Remove Canned message screen if no action is taken for some milliseconds #define INACTIVATE_AFTER_MS 20000 @@ -786,6 +792,11 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event) if (runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) return false; + // === 中文输入模式处理 === + if (inputMode == InputMode::CHINESE) { + return handleChineseInput(event); + } + #if defined(USE_VIRTUAL_KEYBOARD) // Cancel (dismiss freetext screen) if (event->inputEvent == INPUT_BROKER_LEFT) { @@ -961,12 +972,12 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event) return true; } - // '#' key: cycle input mode DIGIT -> LOWER -> UPPER -> DIGIT ... + // '#' key: cycle input mode DIGIT -> LOWER -> UPPER -> CHINESE -> DIGIT ... if (event->kbchar == '#') { // First, commit any pending multi-tap character commitMultiTap(); int m = static_cast(inputMode); - inputMode = static_cast((m + 1) % 3); + inputMode = static_cast((m + 1) % 4); // 修改为 %4,支持4种模式 LOG_DEBUG("[T9] Input mode: %d", (int)inputMode); lastTouchMillis = millis(); // Use REDRAW_ONLY: commitMultiTap() already triggered REGENERATE_FRAMESET, @@ -1080,6 +1091,96 @@ int CannedMessageModule::handleEmotePickerInput(const InputEvent *event) return 0; } +// === 中文输入法:数字选择汉字 === +bool CannedMessageModule::handleChineseInput(const InputEvent *event) +{ + // 数字键1-6:选择对应的汉字 + if (event->kbchar >= '1' && event->kbchar <= '6') { + // 常用汉字列表(6个) + static const char* chineseChars[] = {"我", "你", "他", "是", "的", "好"}; + int index = event->kbchar - '1'; + + // 插入选中的汉字到 freetext + String chineseChar = chineseChars[index]; + if (cursor >= freetext.length()) { + freetext += chineseChar; + } else { + freetext = freetext.substring(0, cursor) + chineseChar + freetext.substring(cursor); + } + cursor += chineseChar.length(); + + lastTouchMillis = millis(); + screen->forceDisplay(); + return true; + } + + // * 键或 BACK 键:删除最后一个字符 + if (event->kbchar == '*' || event->inputEvent == INPUT_BROKER_BACK) { + if (freetext.length() > 0) { + // 简化处理:删除最后1-3个字节(UTF-8中文是3字节) + // 找到最后一个UTF-8字符的起始位置 + int lastPos = freetext.length() - 1; + while (lastPos >= 0 && (freetext[lastPos] & 0xC0) == 0x80) { + lastPos--; // 向前找到UTF-8字符的起始字节 + } + if (lastPos >= 0) { + freetext.remove(lastPos); + if (cursor > lastPos) { + cursor = lastPos; + } + } + } + lastTouchMillis = millis(); + screen->forceDisplay(); + return true; + } + + // SELECT 键:切换回其他输入模式(或确认输入) + if (isSelectEvent(event)) { + // 切换到小写字母模式,继续输入 + inputMode = InputMode::LOWER; + lastTouchMillis = millis(); + screen->forceDisplay(); + return true; + } + + // 其他按键:不处理 + return false; +} + +// === 绘制中文输入界面 === +void CannedMessageModule::drawChineseInput(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + // 常用汉字列表(6个) + static const char* chineseChars[] = {"我", "你", "他", "是", "的", "好"}; + + // 设置字体(使用支持中文的字体) + display->setFont(FONT_MEDIUM); + display->setTextAlignment(TEXT_ALIGN_LEFT); + + // 绘制标题 + display->drawString(0, 0, "中文输入:"); + + // 绘制已输入的文本 + if (freetext.length() > 0) { + display->drawString(0, 16, "文本:" + freetext); + } + + // 绘制候选汉字(在屏幕底部) + int startY = display->getHeight() - 16; // 从底部向上绘制 + int xPos = 0; + + for (int i = 0; i < 6; i++) { + String label = String(i+1) + ":" + String(chineseChars[i]); + display->drawString(xPos, startY, label); + xPos += 40; // 每个候选字间隔40像素 + if (xPos > display->getWidth() - 40) { + xPos = 0; + startY -= 16; // 换行 + } + } +} + void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const char *message, bool wantReplies) { lastDest = dest; @@ -1906,6 +2007,13 @@ void CannedMessageModule::drawEmotePickerScreen(OLEDDisplay *display, OLEDDispla void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { this->displayHeight = display->getHeight(); // Store display height for later use + + // === 中文输入模式界面 === + if (inputMode == InputMode::CHINESE) { + drawChineseInput(display, state, x, y); + return; + } + char buffer[50]; display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); diff --git a/code/firmware-2.7.15.567b8ea/src/modules/CannedMessageModule.h b/code/firmware-2.7.15.567b8ea/src/modules/CannedMessageModule.h index 2b2f0cb..8c119f0 100644 --- a/code/firmware-2.7.15.567b8ea/src/modules/CannedMessageModule.h +++ b/code/firmware-2.7.15.567b8ea/src/modules/CannedMessageModule.h @@ -194,7 +194,7 @@ class CannedMessageModule : public SinglePortModule, public Observable