Merge remote-tracking branch 'origin/main'
# Conflicts: # PCB/Moonshine_travelers.eprj2
This commit is contained in:
Binary file not shown.
@@ -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"键)和更多汉字
|
||||||
|
|
||||||
@@ -57,6 +57,12 @@ const char *const CannedMessageModule::t9LetterMap[][5] = {
|
|||||||
#include "graphics/fonts/ChineseFont12x12.h"
|
#include "graphics/fonts/ChineseFont12x12.h"
|
||||||
#include <Throttle.h>
|
#include <Throttle.h>
|
||||||
|
|
||||||
|
// === 中文输入法:常用汉字列表(6个)===
|
||||||
|
// 在 handleChineseInput() 和 drawChineseInput() 方法中硬编码使用
|
||||||
|
// 格式:数字键 -> 汉字
|
||||||
|
static const char* chineseChars[] = {"我", "你", "他", "是", "的", "好"};
|
||||||
|
|
||||||
|
|
||||||
// Remove Canned message screen if no action is taken for some milliseconds
|
// Remove Canned message screen if no action is taken for some milliseconds
|
||||||
#define INACTIVATE_AFTER_MS 20000
|
#define INACTIVATE_AFTER_MS 20000
|
||||||
|
|
||||||
@@ -786,6 +792,11 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
|
|||||||
if (runState != CANNED_MESSAGE_RUN_STATE_FREETEXT)
|
if (runState != CANNED_MESSAGE_RUN_STATE_FREETEXT)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// === 中文输入模式处理 ===
|
||||||
|
if (inputMode == InputMode::CHINESE) {
|
||||||
|
return handleChineseInput(event);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(USE_VIRTUAL_KEYBOARD)
|
#if defined(USE_VIRTUAL_KEYBOARD)
|
||||||
// Cancel (dismiss freetext screen)
|
// Cancel (dismiss freetext screen)
|
||||||
if (event->inputEvent == INPUT_BROKER_LEFT) {
|
if (event->inputEvent == INPUT_BROKER_LEFT) {
|
||||||
@@ -961,12 +972,12 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// '#' key: cycle input mode DIGIT -> LOWER -> UPPER -> DIGIT ...
|
// '#' key: cycle input mode DIGIT -> LOWER -> UPPER -> CHINESE -> DIGIT ...
|
||||||
if (event->kbchar == '#') {
|
if (event->kbchar == '#') {
|
||||||
// First, commit any pending multi-tap character
|
// First, commit any pending multi-tap character
|
||||||
commitMultiTap();
|
commitMultiTap();
|
||||||
int m = static_cast<int>(inputMode);
|
int m = static_cast<int>(inputMode);
|
||||||
inputMode = static_cast<InputMode>((m + 1) % 3);
|
inputMode = static_cast<InputMode>((m + 1) % 4); // 修改为 %4,支持4种模式
|
||||||
LOG_DEBUG("[T9] Input mode: %d", (int)inputMode);
|
LOG_DEBUG("[T9] Input mode: %d", (int)inputMode);
|
||||||
lastTouchMillis = millis();
|
lastTouchMillis = millis();
|
||||||
// Use REDRAW_ONLY: commitMultiTap() already triggered REGENERATE_FRAMESET,
|
// Use REDRAW_ONLY: commitMultiTap() already triggered REGENERATE_FRAMESET,
|
||||||
@@ -1080,6 +1091,96 @@ int CannedMessageModule::handleEmotePickerInput(const InputEvent *event)
|
|||||||
return 0;
|
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)
|
void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const char *message, bool wantReplies)
|
||||||
{
|
{
|
||||||
lastDest = dest;
|
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)
|
void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
this->displayHeight = display->getHeight(); // Store display height for later use
|
this->displayHeight = display->getHeight(); // Store display height for later use
|
||||||
|
|
||||||
|
// === 中文输入模式界面 ===
|
||||||
|
if (inputMode == InputMode::CHINESE) {
|
||||||
|
drawChineseInput(display, state, x, y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
char buffer[50];
|
char buffer[50];
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// === Multi-tap T9 input method (for TCA9535 numpad) ===
|
// === Multi-tap T9 input method (for TCA9535 numpad) ===
|
||||||
enum class InputMode : uint8_t { DIGIT, LOWER, UPPER };
|
enum class InputMode : uint8_t { DIGIT, LOWER, UPPER, CHINESE };
|
||||||
InputMode inputMode = InputMode::LOWER; // 默认小写字母模式
|
InputMode inputMode = InputMode::LOWER; // 默认小写字母模式
|
||||||
uint8_t multiTapKey = 0xFF; // 上一次按的数字键 ('0'-'9'), 0xFF = no pending key
|
uint8_t multiTapKey = 0xFF; // 上一次按的数字键 ('0'-'9'), 0xFF = no pending key
|
||||||
uint8_t multiTapIndex = 0; // 当前循环索引
|
uint8_t multiTapIndex = 0; // 当前循环索引
|
||||||
@@ -212,6 +212,15 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
|
|||||||
bool handleFreeTextInput(const InputEvent *event);
|
bool handleFreeTextInput(const InputEvent *event);
|
||||||
bool commitMultiTap(); // Returns true if a character was actually committed
|
bool commitMultiTap(); // Returns true if a character was actually committed
|
||||||
void showMultiTapPreview();
|
void showMultiTapPreview();
|
||||||
|
|
||||||
|
// === 中文输入法相关方法 ===
|
||||||
|
bool handleChineseInput(const InputEvent *event); // 处理中文输入模式的按键
|
||||||
|
void drawChineseInput(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); // 绘制中文输入界面
|
||||||
|
|
||||||
|
// === 中文输入法相关成员变量 ===
|
||||||
|
String chinesePinyin; // 当前输入的拼音
|
||||||
|
String chineseCandidates; // 候选汉字字符串(用逗号分隔)
|
||||||
|
int chineseCandidateIndex; // 当前选中的候选字索引
|
||||||
|
|
||||||
#if defined(USE_VIRTUAL_KEYBOARD)
|
#if defined(USE_VIRTUAL_KEYBOARD)
|
||||||
Letter keyboard[2][4][10] = {{{{"Q", 20, 0, 0, 0, 0},
|
Letter keyboard[2][4][10] = {{{{"Q", 20, 0, 0, 0, 0},
|
||||||
|
|||||||
Reference in New Issue
Block a user