ESP32: ужесточить touch UX и обновить инструкции
This commit is contained in:
parent
b5276890fb
commit
471fde78c1
@ -24,6 +24,13 @@
|
||||
- Подробные служебные правила Telegram-обработчика, его очередь, история, systemd-запуск и особенности ответов описывать в `SHiNE-agent-bot-coder/AGENT.md`.
|
||||
- Если в сообщениях пользователя встречается «агент MD» или похожая формулировка про файл инструкций Codex, считать, что имеется в виду автоматически читаемый `AGENTS.md`.
|
||||
|
||||
## ESP32 UI сабсервера
|
||||
- Для UI-скетча устройства `ESP32-S3-Touch-AMOLED-2.16` документ-спецификация и Arduino-скетч должны всегда оставаться синхронными.
|
||||
- Актуальный документ по экранной логике, состояниям, кнопкам, полям, статусам и ограничениям UI считать источником истины для скетча.
|
||||
- При изменении документа обязательно в том же наборе изменений приводить в соответствие скетч.
|
||||
- При изменении скетча обязательно в том же наборе изменений обновлять документ, если поменялись экраны, тексты, переходы, статусы, кнопки, поля или поведение.
|
||||
- Для нового ESP32 UI-прототипа сабсервера использовать русский язык как основной и отдельно следить, чтобы текст реально отображался на устройстве, а не только логически присутствовал в коде.
|
||||
|
||||
## Solana-модуль
|
||||
- В проекте есть отдельный Solana/Anchor-модуль в папке `shine-solana/shine/`.
|
||||
- Модуль логически связан с SHiNE, но не должен автоматически подключаться к сборке или деплою основного сервера без отдельного решения.
|
||||
|
||||
@ -34,7 +34,10 @@
|
||||
- визуальный курсор в поле ввода не показывается;
|
||||
- новые символы всегда дописываются только в конец строки;
|
||||
- основные 3 ряда клавиш и нижний служебный ряд стали выше;
|
||||
- внизу остаётся отдельная тёмная полоса с версией `NAV v7`, а рамка клавиатурного блока заканчивается выше неё;
|
||||
- внизу остаётся отдельная тёмная полоса с версией `SHiNE subserver (v.0.18)`, а рамка клавиатурного блока заканчивается выше неё;
|
||||
- одно непрерывное касание вызывает не более одного действия кнопки;
|
||||
- скольжение пальцем по клавиатуре не нажимает подряд несколько клавиш;
|
||||
- медленный свайп по экрану не должен превращаться в случайное нажатие кнопки;
|
||||
- `ABC/123`, `SHIFT`, `DEL`, `SAVE`, `CANCEL` работают;
|
||||
- при успехе SSID и пароль сохраняются, а `HOME` показывает `Wi-Fi connected`;
|
||||
- при ошибке показывается `Connection failed`;
|
||||
|
||||
@ -253,7 +253,7 @@
|
||||
- `DEL`
|
||||
- `SAVE`
|
||||
- `CANCEL`
|
||||
- ниже рамки клавиатурного блока остаётся отдельная тёмная полоса с версией `NAV v7`.
|
||||
- ниже рамки клавиатурного блока остаётся отдельная тёмная полоса с версией `SHiNE subserver (v.0.18)`.
|
||||
|
||||
## Жесты
|
||||
|
||||
@ -279,6 +279,9 @@
|
||||
|
||||
- если палец ушёл из зоны обычного тапа и жест определяется как свайп, случайное попадание на кнопку по пути не должно превращать свайп в нажатие;
|
||||
- приоритет у свайпа, а не у `click`.
|
||||
- одно непрерывное касание может вызвать не более одного действия кнопки;
|
||||
- скольжение пальцем по экранной клавиатуре не должно вызывать цепочку из нескольких нажатий;
|
||||
- периодический refresh на `HOME` не должен срабатывать во время активного касания.
|
||||
|
||||
## Язык
|
||||
|
||||
|
||||
@ -137,6 +137,9 @@ static int16_t gTouchLastX = 0;
|
||||
static int16_t gTouchLastY = 0;
|
||||
static SwipeDirection gPendingSwipe = SWIPE_NONE;
|
||||
static bool gBlockClick = false;
|
||||
static bool gSuppressTouchUntilRelease = false;
|
||||
static uint32_t gTouchSequence = 0;
|
||||
static uint32_t gLastHandledTouchSequence = 0;
|
||||
|
||||
static String gWifiSavedSsid;
|
||||
static String gWifiSavedPassword;
|
||||
@ -257,13 +260,23 @@ static void lvglTouchRead(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) {
|
||||
if (touching) {
|
||||
if (!gTouchDown) {
|
||||
gTouchDown = true;
|
||||
gTouchSequence++;
|
||||
gTouchStartX = x;
|
||||
gTouchStartY = y;
|
||||
gBlockClick = false;
|
||||
gSuppressTouchUntilRelease = false;
|
||||
}
|
||||
gTouchLastX = x;
|
||||
gTouchLastY = y;
|
||||
if (movedTooFarForTap(gTouchStartX, gTouchStartY, gTouchLastX, gTouchLastY)) {
|
||||
gBlockClick = true;
|
||||
gSuppressTouchUntilRelease = true;
|
||||
}
|
||||
if (gSuppressTouchUntilRelease) {
|
||||
data->state = LV_INDEV_STATE_REL;
|
||||
data->point.x = gTouchLastX;
|
||||
data->point.y = gTouchLastY;
|
||||
return;
|
||||
}
|
||||
data->state = LV_INDEV_STATE_PR;
|
||||
data->point.x = x;
|
||||
@ -274,11 +287,11 @@ static void lvglTouchRead(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) {
|
||||
if (gTouchDown) {
|
||||
gTouchDown = false;
|
||||
gPendingSwipe = detectSwipe(gTouchStartX, gTouchStartY, gTouchLastX, gTouchLastY);
|
||||
if (gPendingSwipe == SWIPE_NONE && !movedTooFarForTap(gTouchStartX, gTouchStartY, gTouchLastX, gTouchLastY)) {
|
||||
gBlockClick = false;
|
||||
}
|
||||
}
|
||||
|
||||
gSuppressTouchUntilRelease = false;
|
||||
gBlockClick = false;
|
||||
|
||||
data->state = LV_INDEV_STATE_REL;
|
||||
data->point.x = gTouchLastX;
|
||||
data->point.y = gTouchLastY;
|
||||
@ -800,9 +813,12 @@ static void restoreTextareaFromEditValue() {
|
||||
}
|
||||
|
||||
static void networkSelectCb(lv_event_t *event) {
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED || gBlockClick) {
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED || gBlockClick || gLastHandledTouchSequence == gTouchSequence) {
|
||||
return;
|
||||
}
|
||||
gLastHandledTouchSequence = gTouchSequence;
|
||||
gSuppressTouchUntilRelease = true;
|
||||
gBlockClick = true;
|
||||
|
||||
int index = static_cast<int>(reinterpret_cast<uintptr_t>(lv_event_get_user_data(event)));
|
||||
if (index < 0 || index >= gScanResultCount) {
|
||||
@ -819,9 +835,12 @@ static void networkSelectCb(lv_event_t *event) {
|
||||
}
|
||||
|
||||
static void editorKeyCb(lv_event_t *event) {
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED || gBlockClick || !gInputTextArea) {
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED || gBlockClick || gLastHandledTouchSequence == gTouchSequence || !gInputTextArea) {
|
||||
return;
|
||||
}
|
||||
gLastHandledTouchSequence = gTouchSequence;
|
||||
gSuppressTouchUntilRelease = true;
|
||||
gBlockClick = true;
|
||||
|
||||
const char *token = static_cast<const char *>(lv_event_get_user_data(event));
|
||||
if (!token) {
|
||||
@ -874,9 +893,12 @@ static void editorKeyCb(lv_event_t *event) {
|
||||
}
|
||||
|
||||
static void actionButtonCb(lv_event_t *event) {
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED || gBlockClick) {
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED || gBlockClick || gLastHandledTouchSequence == gTouchSequence) {
|
||||
return;
|
||||
}
|
||||
gLastHandledTouchSequence = gTouchSequence;
|
||||
gSuppressTouchUntilRelease = true;
|
||||
gBlockClick = true;
|
||||
|
||||
ActionId action = static_cast<ActionId>(reinterpret_cast<uintptr_t>(lv_event_get_user_data(event)));
|
||||
switch (action) {
|
||||
@ -1665,7 +1687,7 @@ void loop() {
|
||||
manageWifiReconnect();
|
||||
|
||||
static unsigned long lastHomeRefreshMs = 0;
|
||||
if (gCurrentScreen == SCREEN_HOME && millis() - lastHomeRefreshMs >= 1000) {
|
||||
if (gCurrentScreen == SCREEN_HOME && !gTouchDown && millis() - lastHomeRefreshMs >= 1000) {
|
||||
lastHomeRefreshMs = millis();
|
||||
rebuildScreen();
|
||||
}
|
||||
@ -1690,7 +1712,6 @@ void loop() {
|
||||
SwipeDirection swipe = gPendingSwipe;
|
||||
gPendingSwipe = SWIPE_NONE;
|
||||
handleSwipe(swipe);
|
||||
gBlockClick = false;
|
||||
}
|
||||
|
||||
delay(5);
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
client.version=1.2.146
|
||||
server.version=1.2.138
|
||||
client.version=1.2.147
|
||||
server.version=1.2.139
|
||||
|
||||
Loading…
Reference in New Issue
Block a user