From 0900a540cbdd197a326543819347dfcd60380ee2 Mon Sep 17 00:00:00 2001 From: kevin Date: Sun, 10 May 2026 23:09:28 +0800 Subject: [PATCH] Signed-off-by: kevin --- PCB/Moonshine_PCB.eprj2 | Bin 4358144 -> 4358144 bytes PCB/Moonshine_travelers.eprj2 | Bin 7090176 -> 7102464 bytes .../.workbuddy/memory/2026-05-03.md | 30 +++++ .../src/modules/CannedMessageModule.cpp | 112 +++++++++++++++++- .../src/modules/CannedMessageModule.h | 11 +- 5 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 code/firmware-2.7.15.567b8ea/.workbuddy/memory/2026-05-03.md diff --git a/PCB/Moonshine_PCB.eprj2 b/PCB/Moonshine_PCB.eprj2 index cef4cf2747d8651efc0db11e76940c80aa8f8967..a7fb1118f9c437020cfa019470675f5abe075e78 100644 GIT binary patch delta 219 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�Zu}AIWE6!b4+_~DI)2+M z>_H1(NCE z#d>NgQ5=sj%?PZ`l3OR!#%fvwwc zY@OR*6s>hB)g!t)Z^Kz?zIqvCy^SX8(|n9@%EmRDDFXUx}0^9!93e=<~e9ClPOUzg2Ux3i;T>H4bL2uqP@b^Eu4mCNo2$gY{(xkYlz!_(5Dl)Kl8;-qS zK1b*ijw*1-nRStYzT0s0J=g`M`b3iu8glk~kPP(QhNJJLT%T|Whi068Nn~K}HX3`U zCEP@xa0!QIoPAAXVDB~@dk+$LQ+>i65E^v$b&-L-+i>(fSi@Ocg*#?A?98LB4F26l z^Y7G$GFyeXAewge+lmbS-G=k;rQ%lMx)#nlb2E{_zuR#BJ=hseZnXqA-r=w_%OZn- zx8VeQA-9X@uWiC592|CLS!5vaHXMO3#kUF9Drnf*A5&!D?=~8Lr*U>8Q(tL zbLBG5fwRu6iwp$bfg|vxsqLZ#zXNBTSrQray91}+g9U$dhu{>>L8H#RjB{iV@D7}S zzl@dc5G`5Yv@^>h1Auqn0DNg|hj0#plg=!O4F27L^Y1}RDen;Mm>oFn%(BSf-yJyr zPHTQ*zovGG&fy$5?##N#fZ!cC1Ye5x3wscpbY@9p0PqeRfG-5;)PCWDAC5b-E;0ak z2M)krUd4%Zf#c4siwp|hfm86oaYDI2BUs$RX=j#21_bZGA^1{!r*LF})6Oi34F27L z^X~yZIkHnY3Bhq^)EiVXPOfy3{?lD{Z!DsbGHn~Dqq-a!-anG@miE-zf(txpQp zws78=b&)~AJ8%j<_(t1_TSUikaNL^Rmo8iba>mmb!ci<3wFtB9OqJafYJhLn^KzIiZ z!k2PsBP*DK;IuPKB7=c<&DS;+v+}U5o$pGLTGyqS_m8o0j3%=LZf#c5H zPGk`94xE7hC?lME;Gi=rB7=W-;QV_K){SO_`(-rf?DrlS{JVqZ-}%9t8G7ADuiNQ$ z2O-*b3eQ^L;4`-!87#bmX5mSr+@18gi(Y%^OZoSPQ-To~PC>IiGPrmL&Bc>G<-ML> z-Y0rMl>^71Sr-{_yn}}0Ntdy2c>37g;c-EGfpgHTiwrv6LDTW1OKLwoGUpy)dqKm{ z{#Yynk9XjBd;s|0JN?O(&1DqMLvwSHfyg^(M4rDq;a)u&t~Jm9@w@}e=_&JXnBIBw z!1iAWWP!-y z14pD;8yVpIC>oq6Z8C@Gb(mg9Xx%p~df3n?oR?;OWDxYDaDu*69u{q*M&YP5OCp1y zA4L=N)MTcH>2)8yj(Ymk{lX>#N2XaH86^EEoTM*IG|}_uM;@RD9XxpHw5e5p7^?n| hrmR1!uKv!hQ92?^r(fRI-tK;49*BBBk3G~9{vSQ7)NB9% 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