ESP32: зафиксировать промежуточный NAV v6 UI прототип
This commit is contained in:
parent
32606fe1c2
commit
e385bb6bf9
@ -1,17 +1,44 @@
|
|||||||
# ESP32 nav minimal test
|
# ESP32 nav minimal test
|
||||||
|
|
||||||
- Краткое описание: новый минимальный навигационный UI-прототип для сабсервера на базе рабочего `LVGL + subserver touch`.
|
- Краткое описание: минимальный UI-прототип для сабсервера на базе `LVGL + subserver touch`, с Wi-Fi flow, серверными адресами и общим экраном редактирования текста.
|
||||||
- Что проверять:
|
- Что проверять:
|
||||||
- стартует экран `HOME`;
|
- стартует экран `HOME`;
|
||||||
- на `HOME` видны `login`, `subserver1`, `W BAT`, `STATUS`, кнопка `SETTINGS`;
|
- на `HOME` видны `login`, `subserver1`, `W BAT`, `STATUS`, Wi-Fi статус и кнопка `SETTINGS`;
|
||||||
|
- Wi-Fi статус на `HOME` корректно показывает одно из состояний:
|
||||||
|
- `Wi-Fi not configured`
|
||||||
|
- `Wi-Fi disconnected`
|
||||||
|
- `Wi-Fi connected`
|
||||||
- кнопка `SETTINGS` открывает `SETTINGS_MENU`;
|
- кнопка `SETTINGS` открывает `SETTINGS_MENU`;
|
||||||
- свайп влево на `HOME` открывает `SETTINGS_MENU`;
|
- свайп влево на `HOME` открывает `SETTINGS_MENU`;
|
||||||
- в `SETTINGS_MENU` сначала видны только `Wi-Fi` и `Server`;
|
- в `SETTINGS_MENU` сначала видны только `Wi-Fi` и `Server`;
|
||||||
|
- обе видимые карточки меню одного цвета;
|
||||||
- свайп вверх показывает `Server` и `Account`;
|
- свайп вверх показывает `Server` и `Account`;
|
||||||
- свайп вниз возвращает `Wi-Fi` и `Server`;
|
- свайп вниз возвращает `Wi-Fi` и `Server`;
|
||||||
- свайп вправо из `SETTINGS_MENU` возвращает на `HOME`;
|
- свайп вправо из `SETTINGS_MENU` возвращает на `HOME`;
|
||||||
- нажатия по `Wi-Fi`, `Server`, `Account` открывают соответствующие экраны;
|
- нажатие `Wi-Fi` открывает `WIFI_SCREEN`;
|
||||||
|
- `SELECT NETWORK` запускает скан;
|
||||||
|
- после скана показывается список доступных SSID;
|
||||||
|
- выбор SSID открывает общий экран редактирования текста для пароля;
|
||||||
|
- на этом экране видно старое значение, курсор стоит в конце;
|
||||||
|
- две верхние служебные строки над полем ввода отсутствуют;
|
||||||
|
- большая клавиатура реально видна на экране и занимает большую часть высоты;
|
||||||
|
- буквы разбиты на 2 страницы;
|
||||||
|
- режим символов тоже разбит на 2 страницы;
|
||||||
|
- на правой странице кнопки стоят в ровных вертикальных колонках;
|
||||||
|
- свайп влево/вправо на экране ввода переключает страницы клавиатуры;
|
||||||
|
- при этом свайп страниц клавиатуры срабатывает только из нижней клавиатурной зоны, а не из верхней части экрана;
|
||||||
|
- `ABC/123`, `SHIFT`, `DEL`, `SAVE`, `CANCEL` работают;
|
||||||
|
- при успехе SSID и пароль сохраняются, а `HOME` показывает `Wi-Fi connected`;
|
||||||
|
- при ошибке показывается `Connection failed`;
|
||||||
|
- `CLEAR SAVED WI-FI` очищает сохранённые настройки;
|
||||||
|
- нажатие `Server` открывает `SERVER_SCREEN`;
|
||||||
|
- в `SERVER_SCREEN` видны и редактируются два значения:
|
||||||
|
- `https://api.devnet.solana.com`
|
||||||
|
- `https://shineup.me`
|
||||||
|
- нажатие `SOLANA RPC` открывает общий экран редактирования;
|
||||||
|
- нажатие `SHINE SERVER` открывает общий экран редактирования;
|
||||||
|
- после `SAVE` новые адреса сохраняются в NVS;
|
||||||
- свайп вправо из внутренних экранов возвращает в `SETTINGS_MENU`;
|
- свайп вправо из внутренних экранов возвращает в `SETTINGS_MENU`;
|
||||||
- если во время реального свайпа палец проходит по кнопке, это не должно открывать кнопку как обычный `click`.
|
- если во время реального свайпа палец проходит по кнопке, это не должно открывать кнопку как обычный `click`.
|
||||||
- Ожидаемый результат: новый скетч даёт чистый навигационный каркас без старой перегруженной логики.
|
- Ожидаемый результат: новый скетч даёт чистый навигационный каркас и уже умеет настраивать Wi-Fi и серверные адреса на самой ESP32.
|
||||||
- Статус: pending
|
- Статус: pending
|
||||||
|
|||||||
@ -4,12 +4,11 @@
|
|||||||
|
|
||||||
## Цель
|
## Цель
|
||||||
|
|
||||||
Этот прототип проверяет только базовую механику экранов, крупных кнопок и свайпов.
|
Этот прототип проверяет базовую механику экранов, крупных кнопок, свайпов, первичную настройку Wi-Fi и настройку серверных адресов через общий экран редактирования текста.
|
||||||
|
|
||||||
На этом этапе отсутствуют:
|
На этом этапе отсутствуют:
|
||||||
- реальный Wi-Fi;
|
- логика серверной проверки доступности;
|
||||||
- реальные серверные проверки;
|
- логин/пароль учётной записи SHiNE;
|
||||||
- логин/пароль;
|
|
||||||
- PIN;
|
- PIN;
|
||||||
- кошелёк;
|
- кошелёк;
|
||||||
- QR;
|
- QR;
|
||||||
@ -20,12 +19,13 @@
|
|||||||
|
|
||||||
## Экраны
|
## Экраны
|
||||||
|
|
||||||
Прототип содержит 5 экранов:
|
Прототип содержит 6 экранов:
|
||||||
- `HOME`
|
- `HOME`
|
||||||
- `SETTINGS_MENU`
|
- `SETTINGS_MENU`
|
||||||
- `WIFI_SCREEN`
|
- `WIFI_SCREEN`
|
||||||
- `SERVER_SCREEN`
|
- `SERVER_SCREEN`
|
||||||
- `ACCOUNT_SCREEN`
|
- `ACCOUNT_SCREEN`
|
||||||
|
- `TEXT_EDIT_SCREEN`
|
||||||
|
|
||||||
## HOME
|
## HOME
|
||||||
|
|
||||||
@ -34,8 +34,15 @@
|
|||||||
- ниже `subserver1`;
|
- ниже `subserver1`;
|
||||||
- сверху справа простые индикаторы `W BAT`;
|
- сверху справа простые индикаторы `W BAT`;
|
||||||
- по центру крупный текст `STATUS`;
|
- по центру крупный текст `STATUS`;
|
||||||
|
- статус Wi-Fi;
|
||||||
|
- сохранённый SSID или пометку, что он не сохранён;
|
||||||
- снизу большую кнопку `SETTINGS`.
|
- снизу большую кнопку `SETTINGS`.
|
||||||
|
|
||||||
|
Статусы Wi-Fi на `HOME`:
|
||||||
|
- `Wi-Fi not configured`
|
||||||
|
- `Wi-Fi disconnected`
|
||||||
|
- `Wi-Fi connected`
|
||||||
|
|
||||||
Переходы:
|
Переходы:
|
||||||
- кнопка `SETTINGS` -> `SETTINGS_MENU`;
|
- кнопка `SETTINGS` -> `SETTINGS_MENU`;
|
||||||
- свайп влево -> `SETTINGS_MENU`.
|
- свайп влево -> `SETTINGS_MENU`.
|
||||||
@ -61,6 +68,8 @@
|
|||||||
- `Wi-Fi`
|
- `Wi-Fi`
|
||||||
- `Server`
|
- `Server`
|
||||||
|
|
||||||
|
Обе видимые карточки меню имеют одинаковый цвет.
|
||||||
|
|
||||||
Переходы:
|
Переходы:
|
||||||
- нажатие `Wi-Fi` -> `WIFI_SCREEN`;
|
- нажатие `Wi-Fi` -> `WIFI_SCREEN`;
|
||||||
- нажатие `Server` -> `SERVER_SCREEN`;
|
- нажатие `Server` -> `SERVER_SCREEN`;
|
||||||
@ -69,23 +78,52 @@
|
|||||||
|
|
||||||
## WIFI_SCREEN
|
## WIFI_SCREEN
|
||||||
|
|
||||||
|
Экран содержит 2 внутренних режима.
|
||||||
|
|
||||||
|
### 1. Overview
|
||||||
|
|
||||||
Показывает:
|
Показывает:
|
||||||
- `Wi-Fi`
|
- текущий Wi-Fi статус;
|
||||||
- `Wi-Fi screen`
|
- сохранённый SSID;
|
||||||
|
- статусное сообщение;
|
||||||
|
- кнопку `SELECT NETWORK`;
|
||||||
|
- кнопку `CLEAR SAVED WI-FI`;
|
||||||
|
- кнопку `BACK`.
|
||||||
|
|
||||||
|
### 2. Scan Results
|
||||||
|
|
||||||
|
После `SELECT NETWORK` выполняется скан доступных Wi-Fi сетей.
|
||||||
|
|
||||||
|
Показывает:
|
||||||
|
- заголовок `SELECT NETWORK`;
|
||||||
|
- количество или результат сканирования;
|
||||||
|
- список найденных SSID как крупные кнопки;
|
||||||
|
- кнопку `SCAN AGAIN`;
|
||||||
|
- кнопку `BACK`.
|
||||||
|
|
||||||
|
Нажатие на SSID открывает `TEXT_EDIT_SCREEN` для ввода пароля.
|
||||||
|
|
||||||
Переходы:
|
Переходы:
|
||||||
- свайп вправо -> `SETTINGS_MENU`
|
- свайп вправо из любого режима `WIFI_SCREEN` -> `SETTINGS_MENU`
|
||||||
- кнопка `BACK` -> `SETTINGS_MENU`
|
- кнопка `BACK` -> `SETTINGS_MENU`
|
||||||
|
|
||||||
## SERVER_SCREEN
|
## SERVER_SCREEN
|
||||||
|
|
||||||
Показывает:
|
Показывает:
|
||||||
- `Server`
|
- статусное сообщение;
|
||||||
- `Server screen`
|
- текущий `Solana RPC` адрес;
|
||||||
|
- кнопку `SOLANA RPC`;
|
||||||
|
- текущий `Shine server` адрес;
|
||||||
|
- кнопку `SHINE SERVER`.
|
||||||
|
|
||||||
|
Значения по умолчанию:
|
||||||
|
- Solana RPC: `https://api.devnet.solana.com`
|
||||||
|
- Shine server: `https://shineup.me`
|
||||||
|
|
||||||
|
Нажатие на любую из двух кнопок открывает `TEXT_EDIT_SCREEN`.
|
||||||
|
|
||||||
Переходы:
|
Переходы:
|
||||||
- свайп вправо -> `SETTINGS_MENU`
|
- свайп вправо -> `SETTINGS_MENU`
|
||||||
- кнопка `BACK` -> `SETTINGS_MENU`
|
|
||||||
|
|
||||||
## ACCOUNT_SCREEN
|
## ACCOUNT_SCREEN
|
||||||
|
|
||||||
@ -97,6 +135,63 @@
|
|||||||
- свайп вправо -> `SETTINGS_MENU`
|
- свайп вправо -> `SETTINGS_MENU`
|
||||||
- кнопка `BACK` -> `SETTINGS_MENU`
|
- кнопка `BACK` -> `SETTINGS_MENU`
|
||||||
|
|
||||||
|
## TEXT_EDIT_SCREEN
|
||||||
|
|
||||||
|
Общий экран редактирования строковых значений.
|
||||||
|
|
||||||
|
Используется для:
|
||||||
|
- пароля Wi-Fi;
|
||||||
|
- Solana RPC;
|
||||||
|
- Shine server.
|
||||||
|
|
||||||
|
Показывает:
|
||||||
|
- заголовок;
|
||||||
|
- поле ввода, уже заполненное старым значением;
|
||||||
|
- курсор установлен в конец текста;
|
||||||
|
- кнопки `SAVE`, `CANCEL`, `DEL`, `CLR`;
|
||||||
|
- большую экранную клавиатуру.
|
||||||
|
|
||||||
|
## Клавиатура
|
||||||
|
|
||||||
|
Клавиатура единая для всех текстовых вводов.
|
||||||
|
|
||||||
|
Особенности:
|
||||||
|
- занимает нижнюю часть экрана;
|
||||||
|
- разбита на 2 страницы;
|
||||||
|
- переключение страниц выполняется свайпом влево/вправо внутри `TEXT_EDIT_SCREEN`;
|
||||||
|
- страница 1 содержит в основном буквы и базовые URL-символы;
|
||||||
|
- страница 2 содержит цифры и дополнительные URL/символьные кнопки;
|
||||||
|
- есть специальные действия `DEL` и `CLR`.
|
||||||
|
|
||||||
|
## Хранение Wi-Fi
|
||||||
|
|
||||||
|
Используется `Preferences` (NVS памяти ESP32):
|
||||||
|
- `wifi_ssid`
|
||||||
|
- `wifi_pass`
|
||||||
|
|
||||||
|
При старте устройства, если сохранён SSID, выполняется попытка подключения к сохранённой сети.
|
||||||
|
|
||||||
|
## Хранение серверов
|
||||||
|
|
||||||
|
Используется `Preferences` (NVS памяти ESP32):
|
||||||
|
- `solana_rpc`
|
||||||
|
- `shine_server`
|
||||||
|
|
||||||
|
## Детали клавиатуры
|
||||||
|
|
||||||
|
- клавиатура занимает примерно `2/3`-`3/4` высоты экрана;
|
||||||
|
- сверху остаются только заголовок, подсказка и поле ввода;
|
||||||
|
- буквы занимают 3 ряда;
|
||||||
|
- половина букв находится на левой странице, половина на правой;
|
||||||
|
- на правой странице кнопки тоже стоят в ровных колонках, без сдвига рядов вправо;
|
||||||
|
- отдельный режим `symbols` тоже разделён на 2 страницы;
|
||||||
|
- четвёртый ряд содержит:
|
||||||
|
- переключение `ABC/123`
|
||||||
|
- `SHIFT`
|
||||||
|
- `DEL`
|
||||||
|
- `SAVE`
|
||||||
|
- `CANCEL`
|
||||||
|
|
||||||
## Жесты
|
## Жесты
|
||||||
|
|
||||||
Поддерживаются направления:
|
Поддерживаются направления:
|
||||||
@ -112,6 +207,8 @@
|
|||||||
- `WIFI_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
- `WIFI_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
||||||
- `SERVER_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
- `SERVER_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
||||||
- `ACCOUNT_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
- `ACCOUNT_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
||||||
|
- `TEXT_EDIT_SCREEN`: свайп влево/вправо -> переключение страниц клавиатуры
|
||||||
|
- переключение страниц клавиатуры срабатывает только если свайп начался в зоне самой клавиатуры, а не по всему экрану редактора
|
||||||
|
|
||||||
## Особенность обработки жестов
|
## Особенность обработки жестов
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
- `lvgl-official-based-test` — наш минимальный экран, но на максимально близкой к официальному `LVGL_Widgets` инициализации
|
- `lvgl-official-based-test` — наш минимальный экран, но на максимально близкой к официальному `LVGL_Widgets` инициализации
|
||||||
- `lvgl-subserver-touch-test` — гибрид: `LVGL`-интерфейс, но display/touch init и raw touch-read взяты из `shine_subserver_ui`; подтверждено на устройстве, touch работает, зелёных линий по краям нет
|
- `lvgl-subserver-touch-test` — гибрид: `LVGL`-интерфейс, но display/touch init и raw touch-read взяты из `shine_subserver_ui`; подтверждено на устройстве, touch работает, зелёных линий по краям нет
|
||||||
- `lvgl-russian-font-test` — тест кастомного `LVGL`-шрифта с кириллицей: русские кнопки, длинные подписи и статусы
|
- `lvgl-russian-font-test` — тест кастомного `LVGL`-шрифта с кириллицей: русские кнопки, длинные подписи и статусы
|
||||||
- `lvgl-nav-minimal-test` — новый минимальный UI-каркас сабсервера: `HOME`, `SETTINGS_MENU`, `Wi-Fi`, `Server`, `Account`, свайпы и крупные кнопки
|
- `lvgl-nav-minimal-test` — новый минимальный UI-каркас сабсервера: `HOME`, `SETTINGS_MENU`, `Wi-Fi`, `Server`, `Account`, свайпы, крупные кнопки и реальная настройка Wi-Fi с сохранением в NVS
|
||||||
|
|
||||||
Запуск:
|
Запуск:
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
- `lvgl_official_based_test/` - минимальный наш экран поверх максимально близкой к официальному `05_LVGL_Widgets` инициализации
|
- `lvgl_official_based_test/` - минимальный наш экран поверх максимально близкой к официальному `05_LVGL_Widgets` инициализации
|
||||||
- `lvgl_subserver_touch_test/` - гибридный тест: `LVGL`-экран с инициализацией дисплея и чтением touch из `shine_subserver_ui`; подтверждён на реальном устройстве
|
- `lvgl_subserver_touch_test/` - гибридный тест: `LVGL`-экран с инициализацией дисплея и чтением touch из `shine_subserver_ui`; подтверждён на реальном устройстве
|
||||||
- `lvgl_russian_font_test/` - тест кастомного кириллического `LVGL`-шрифта с русскими кнопками, длинными строками и рабочим touch
|
- `lvgl_russian_font_test/` - тест кастомного кириллического `LVGL`-шрифта с русскими кнопками, длинными строками и рабочим touch
|
||||||
- `lvgl_nav_minimal_test/` - новый минимальный навигационный каркас сабсервера на рабочем `LVGL + subserver touch`
|
- `lvgl_nav_minimal_test/` - новый минимальный навигационный каркас сабсервера на рабочем `LVGL + subserver touch`, расширенный настройкой Wi-Fi и сохранением в NVS
|
||||||
|
|
||||||
## Запуск
|
## Запуск
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <Preferences.h>
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
#include <Arduino_GFX_Library.h>
|
#include <Arduino_GFX_Library.h>
|
||||||
#include <TouchDrvCSTXXX.hpp>
|
#include <TouchDrvCSTXXX.hpp>
|
||||||
@ -21,7 +23,13 @@
|
|||||||
#define LVGL_TICK_MS 2
|
#define LVGL_TICK_MS 2
|
||||||
#define SWIPE_THRESHOLD 48
|
#define SWIPE_THRESHOLD 48
|
||||||
#define TAP_CANCEL_THRESHOLD 18
|
#define TAP_CANCEL_THRESHOLD 18
|
||||||
#define TEST_VERSION "NAV v2"
|
#define MAX_SCAN_RESULTS 8
|
||||||
|
#define WIFI_CONNECT_TIMEOUT_MS 12000
|
||||||
|
#define TEXT_EDIT_PANEL_X 10
|
||||||
|
#define TEXT_EDIT_PANEL_Y 126
|
||||||
|
#define TEXT_EDIT_PANEL_W 460
|
||||||
|
#define TEXT_EDIT_PANEL_H 344
|
||||||
|
#define TEST_VERSION "NAV v6"
|
||||||
|
|
||||||
enum Screen {
|
enum Screen {
|
||||||
SCREEN_HOME,
|
SCREEN_HOME,
|
||||||
@ -29,6 +37,7 @@ enum Screen {
|
|||||||
SCREEN_WIFI,
|
SCREEN_WIFI,
|
||||||
SCREEN_SERVER,
|
SCREEN_SERVER,
|
||||||
SCREEN_ACCOUNT,
|
SCREEN_ACCOUNT,
|
||||||
|
SCREEN_TEXT_EDIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SwipeDirection {
|
enum SwipeDirection {
|
||||||
@ -47,6 +56,29 @@ enum ActionId {
|
|||||||
ACTION_OPEN_ACCOUNT,
|
ACTION_OPEN_ACCOUNT,
|
||||||
ACTION_BACK_HOME,
|
ACTION_BACK_HOME,
|
||||||
ACTION_BACK_SETTINGS,
|
ACTION_BACK_SETTINGS,
|
||||||
|
ACTION_WIFI_SELECT_NETWORK,
|
||||||
|
ACTION_WIFI_CLEAR,
|
||||||
|
ACTION_SERVER_EDIT_SOLANA,
|
||||||
|
ACTION_SERVER_EDIT_SHINE,
|
||||||
|
ACTION_EDITOR_SAVE,
|
||||||
|
ACTION_EDITOR_CANCEL,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum WifiViewMode {
|
||||||
|
WIFI_VIEW_OVERVIEW,
|
||||||
|
WIFI_VIEW_SCAN_RESULTS,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EditContext {
|
||||||
|
EDIT_CONTEXT_NONE,
|
||||||
|
EDIT_CONTEXT_WIFI_PASSWORD,
|
||||||
|
EDIT_CONTEXT_SOLANA_RPC,
|
||||||
|
EDIT_CONTEXT_SHINE_SERVER,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum KeyboardMode {
|
||||||
|
KEYBOARD_MODE_ALPHA,
|
||||||
|
KEYBOARD_MODE_SYMBOLS,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *kMenuItems[] = {"Wi-Fi", "Server", "Account"};
|
static const char *kMenuItems[] = {"Wi-Fi", "Server", "Account"};
|
||||||
@ -56,12 +88,14 @@ static lv_disp_draw_buf_t gDrawBuf;
|
|||||||
static lv_color_t *gBuf1 = nullptr;
|
static lv_color_t *gBuf1 = nullptr;
|
||||||
static lv_color_t *gBuf2 = nullptr;
|
static lv_color_t *gBuf2 = nullptr;
|
||||||
static lv_obj_t *gRoot = nullptr;
|
static lv_obj_t *gRoot = nullptr;
|
||||||
|
static lv_obj_t *gInputTextArea = nullptr;
|
||||||
|
|
||||||
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
||||||
PIN_LCD_CS, PIN_LCD_SCLK, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
|
PIN_LCD_CS, PIN_LCD_SCLK, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
|
||||||
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||||
gBus, PIN_LCD_RST, 0, DISP_W, DISP_H, 0, 0, 0, 0);
|
gBus, PIN_LCD_RST, 0, DISP_W, DISP_H, 0, 0, 0, 0);
|
||||||
TouchDrvCST92xx gTouch;
|
TouchDrvCST92xx gTouch;
|
||||||
|
Preferences gPrefs;
|
||||||
|
|
||||||
static Screen gCurrentScreen = SCREEN_HOME;
|
static Screen gCurrentScreen = SCREEN_HOME;
|
||||||
static int gSettingsScrollIndex = 0;
|
static int gSettingsScrollIndex = 0;
|
||||||
@ -73,9 +107,48 @@ static int16_t gTouchLastY = 0;
|
|||||||
static SwipeDirection gPendingSwipe = SWIPE_NONE;
|
static SwipeDirection gPendingSwipe = SWIPE_NONE;
|
||||||
static bool gBlockClick = false;
|
static bool gBlockClick = false;
|
||||||
|
|
||||||
|
static String gWifiSavedSsid;
|
||||||
|
static String gWifiSavedPassword;
|
||||||
|
static String gWifiSelectedSsid;
|
||||||
|
static String gWifiStatusMessage = "No Wi-Fi configured";
|
||||||
|
static String gScanResults[MAX_SCAN_RESULTS];
|
||||||
|
static int gScanResultCount = 0;
|
||||||
|
static WifiViewMode gWifiViewMode = WIFI_VIEW_OVERVIEW;
|
||||||
|
|
||||||
|
static String gSolanaRpcUrl = "https://api.devnet.solana.com";
|
||||||
|
static String gShineServerUrl = "https://shineup.me";
|
||||||
|
static String gServerStatusMessage = "Edit RPC or shine host";
|
||||||
|
|
||||||
|
static EditContext gEditContext = EDIT_CONTEXT_NONE;
|
||||||
|
static Screen gEditReturnScreen = SCREEN_HOME;
|
||||||
|
static String gEditTitle;
|
||||||
|
static String gEditHint;
|
||||||
|
static String gEditValue;
|
||||||
|
static int gKeyboardPage = 0;
|
||||||
|
static bool gEditIsPassword = false;
|
||||||
|
static KeyboardMode gKeyboardMode = KEYBOARD_MODE_ALPHA;
|
||||||
|
static bool gKeyboardShift = false;
|
||||||
|
|
||||||
static void rebuildScreen();
|
static void rebuildScreen();
|
||||||
static void showScreen(Screen screen);
|
static void showScreen(Screen screen);
|
||||||
static void handleSwipe(SwipeDirection swipe);
|
static void handleSwipe(SwipeDirection swipe);
|
||||||
|
static void loadPrefs();
|
||||||
|
static void saveWifiPrefs();
|
||||||
|
static void clearWifiPrefs();
|
||||||
|
static void saveServerPrefs();
|
||||||
|
static void beginSavedWifi();
|
||||||
|
static void scanWifiNetworks();
|
||||||
|
static bool connectWifiNow(const String &ssid, const String &password);
|
||||||
|
static String wifiHomeStatus();
|
||||||
|
static String wifiSavedLabel();
|
||||||
|
static void openEditor(EditContext context,
|
||||||
|
Screen returnScreen,
|
||||||
|
const String &title,
|
||||||
|
const String &hint,
|
||||||
|
const String &value,
|
||||||
|
bool isPassword);
|
||||||
|
static void applyEditorValue();
|
||||||
|
static bool isTextEditKeyboardSwipeArea(int16_t x, int16_t y);
|
||||||
|
|
||||||
static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) {
|
static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) {
|
||||||
uint32_t w = area->x2 - area->x1 + 1;
|
uint32_t w = area->x2 - area->x1 + 1;
|
||||||
@ -177,12 +250,242 @@ static lv_obj_t *makeBody(const char *text, lv_coord_t y, lv_coord_t width) {
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void actionButtonCb(lv_event_t *event) {
|
static void showMessageAt(const String &text, lv_coord_t y) {
|
||||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
|
lv_obj_t *label = lv_label_create(gRoot);
|
||||||
|
lv_label_set_text(label, text.c_str());
|
||||||
|
lv_obj_set_width(label, 420);
|
||||||
|
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
|
||||||
|
lv_obj_set_style_text_font(label, &lv_font_montserrat_16, 0);
|
||||||
|
lv_obj_set_style_text_color(label, lv_color_hex(0xB8C6D3), 0);
|
||||||
|
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loadPrefs() {
|
||||||
|
gWifiSavedSsid = gPrefs.getString("wifi_ssid", "");
|
||||||
|
gWifiSavedPassword = gPrefs.getString("wifi_pass", "");
|
||||||
|
gSolanaRpcUrl = gPrefs.getString("solana_rpc", "https://api.devnet.solana.com");
|
||||||
|
gShineServerUrl = gPrefs.getString("shine_server", "https://shineup.me");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void saveWifiPrefs() {
|
||||||
|
gPrefs.putString("wifi_ssid", gWifiSavedSsid);
|
||||||
|
gPrefs.putString("wifi_pass", gWifiSavedPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void saveServerPrefs() {
|
||||||
|
gPrefs.putString("solana_rpc", gSolanaRpcUrl);
|
||||||
|
gPrefs.putString("shine_server", gShineServerUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearWifiPrefs() {
|
||||||
|
gWifiSavedSsid = "";
|
||||||
|
gWifiSavedPassword = "";
|
||||||
|
gWifiSelectedSsid = "";
|
||||||
|
saveWifiPrefs();
|
||||||
|
WiFi.disconnect(true, false);
|
||||||
|
gWifiStatusMessage = "Saved Wi-Fi cleared";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void beginSavedWifi() {
|
||||||
|
if (gWifiSavedSsid.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gBlockClick) {
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.begin(gWifiSavedSsid.c_str(), gWifiSavedPassword.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
static String wifiHomeStatus() {
|
||||||
|
if (gWifiSavedSsid.isEmpty()) {
|
||||||
|
return "Wi-Fi not configured";
|
||||||
|
}
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
return "Wi-Fi connected";
|
||||||
|
}
|
||||||
|
return "Wi-Fi disconnected";
|
||||||
|
}
|
||||||
|
|
||||||
|
static String wifiSavedLabel() {
|
||||||
|
if (gWifiSavedSsid.isEmpty()) {
|
||||||
|
return "Saved: none";
|
||||||
|
}
|
||||||
|
return String("Saved: ") + gWifiSavedSsid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scanWifiNetworks() {
|
||||||
|
gScanResultCount = 0;
|
||||||
|
gWifiStatusMessage = "Scanning networks...";
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.disconnect(false, false);
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
int found = WiFi.scanNetworks();
|
||||||
|
if (found <= 0) {
|
||||||
|
gWifiStatusMessage = "No networks found";
|
||||||
|
gWifiViewMode = WIFI_VIEW_OVERVIEW;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gScanResultCount = min(found, MAX_SCAN_RESULTS);
|
||||||
|
for (int i = 0; i < gScanResultCount; ++i) {
|
||||||
|
gScanResults[i] = WiFi.SSID(i);
|
||||||
|
}
|
||||||
|
WiFi.scanDelete();
|
||||||
|
gWifiStatusMessage = String("Found ") + found + " networks";
|
||||||
|
gWifiViewMode = WIFI_VIEW_SCAN_RESULTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool connectWifiNow(const String &ssid, const String &password) {
|
||||||
|
gWifiStatusMessage = String("Connecting to ") + ssid + "...";
|
||||||
|
rebuildScreen();
|
||||||
|
lv_timer_handler();
|
||||||
|
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.disconnect(false, false);
|
||||||
|
delay(150);
|
||||||
|
WiFi.begin(ssid.c_str(), password.c_str());
|
||||||
|
|
||||||
|
unsigned long started = millis();
|
||||||
|
while (millis() - started < WIFI_CONNECT_TIMEOUT_MS) {
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
gWifiSavedSsid = ssid;
|
||||||
|
gWifiSavedPassword = password;
|
||||||
|
saveWifiPrefs();
|
||||||
|
gWifiStatusMessage = String("Connected: ") + WiFi.localIP().toString();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
delay(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFi.disconnect(false, false);
|
||||||
|
gWifiStatusMessage = "Connection failed";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void openEditor(EditContext context,
|
||||||
|
Screen returnScreen,
|
||||||
|
const String &title,
|
||||||
|
const String &hint,
|
||||||
|
const String &value,
|
||||||
|
bool isPassword) {
|
||||||
|
gEditContext = context;
|
||||||
|
gEditReturnScreen = returnScreen;
|
||||||
|
gEditTitle = title;
|
||||||
|
gEditHint = hint;
|
||||||
|
gEditValue = value;
|
||||||
|
gEditIsPassword = isPassword;
|
||||||
|
gKeyboardPage = 0;
|
||||||
|
gKeyboardMode = KEYBOARD_MODE_ALPHA;
|
||||||
|
gKeyboardShift = false;
|
||||||
|
showScreen(SCREEN_TEXT_EDIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void applyEditorValue() {
|
||||||
|
String value = gInputTextArea ? String(lv_textarea_get_text(gInputTextArea)) : gEditValue;
|
||||||
|
|
||||||
|
if (gEditContext == EDIT_CONTEXT_WIFI_PASSWORD) {
|
||||||
|
bool ok = connectWifiNow(gWifiSelectedSsid, value);
|
||||||
|
gWifiViewMode = WIFI_VIEW_OVERVIEW;
|
||||||
|
if (ok) {
|
||||||
|
showScreen(SCREEN_HOME);
|
||||||
|
} else {
|
||||||
|
showScreen(SCREEN_WIFI);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gEditContext == EDIT_CONTEXT_SOLANA_RPC) {
|
||||||
|
gSolanaRpcUrl = value;
|
||||||
|
saveServerPrefs();
|
||||||
|
gServerStatusMessage = "Solana RPC saved";
|
||||||
|
showScreen(SCREEN_SERVER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gEditContext == EDIT_CONTEXT_SHINE_SERVER) {
|
||||||
|
gShineServerUrl = value;
|
||||||
|
saveServerPrefs();
|
||||||
|
gServerStatusMessage = "Shine server saved";
|
||||||
|
showScreen(SCREEN_SERVER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cancelEditor() {
|
||||||
|
if (gEditContext == EDIT_CONTEXT_WIFI_PASSWORD) {
|
||||||
|
showScreen(SCREEN_WIFI);
|
||||||
|
} else {
|
||||||
|
showScreen(gEditReturnScreen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void networkSelectCb(lv_event_t *event) {
|
||||||
|
if (lv_event_get_code(event) != LV_EVENT_CLICKED || gBlockClick) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = static_cast<int>(reinterpret_cast<uintptr_t>(lv_event_get_user_data(event)));
|
||||||
|
if (index < 0 || index >= gScanResultCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gWifiSelectedSsid = gScanResults[index];
|
||||||
|
openEditor(EDIT_CONTEXT_WIFI_PASSWORD,
|
||||||
|
SCREEN_WIFI,
|
||||||
|
"ENTER PASSWORD",
|
||||||
|
String("SSID: ") + gWifiSelectedSsid,
|
||||||
|
"",
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void editorKeyCb(lv_event_t *event) {
|
||||||
|
if (lv_event_get_code(event) != LV_EVENT_CLICKED || gBlockClick || !gInputTextArea) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *token = static_cast<const char *>(lv_event_get_user_data(event));
|
||||||
|
if (!token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(token, "<DEL>") == 0) {
|
||||||
|
lv_textarea_del_char(gInputTextArea);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (strcmp(token, "<CLR>") == 0) {
|
||||||
|
lv_textarea_set_text(gInputTextArea, "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (strcmp(token, "<SAVE>") == 0) {
|
||||||
|
applyEditorValue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (strcmp(token, "<CANCEL>") == 0) {
|
||||||
|
cancelEditor();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (strcmp(token, "<MODE>") == 0) {
|
||||||
|
gKeyboardMode = (gKeyboardMode == KEYBOARD_MODE_ALPHA) ? KEYBOARD_MODE_SYMBOLS : KEYBOARD_MODE_ALPHA;
|
||||||
|
gKeyboardPage = 0;
|
||||||
|
rebuildScreen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (strcmp(token, "<SHIFT>") == 0) {
|
||||||
|
gKeyboardShift = !gKeyboardShift;
|
||||||
|
rebuildScreen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String out = token;
|
||||||
|
if (gKeyboardMode == KEYBOARD_MODE_ALPHA && gKeyboardShift) {
|
||||||
|
out.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_textarea_add_text(gInputTextArea, out.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void actionButtonCb(lv_event_t *event) {
|
||||||
|
if (lv_event_get_code(event) != LV_EVENT_CLICKED || gBlockClick) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,6 +495,7 @@ static void actionButtonCb(lv_event_t *event) {
|
|||||||
showScreen(SCREEN_SETTINGS_MENU);
|
showScreen(SCREEN_SETTINGS_MENU);
|
||||||
break;
|
break;
|
||||||
case ACTION_OPEN_WIFI:
|
case ACTION_OPEN_WIFI:
|
||||||
|
gWifiViewMode = WIFI_VIEW_OVERVIEW;
|
||||||
showScreen(SCREEN_WIFI);
|
showScreen(SCREEN_WIFI);
|
||||||
break;
|
break;
|
||||||
case ACTION_OPEN_SERVER:
|
case ACTION_OPEN_SERVER:
|
||||||
@ -204,8 +508,40 @@ static void actionButtonCb(lv_event_t *event) {
|
|||||||
showScreen(SCREEN_HOME);
|
showScreen(SCREEN_HOME);
|
||||||
break;
|
break;
|
||||||
case ACTION_BACK_SETTINGS:
|
case ACTION_BACK_SETTINGS:
|
||||||
|
gWifiViewMode = WIFI_VIEW_OVERVIEW;
|
||||||
showScreen(SCREEN_SETTINGS_MENU);
|
showScreen(SCREEN_SETTINGS_MENU);
|
||||||
break;
|
break;
|
||||||
|
case ACTION_WIFI_SELECT_NETWORK:
|
||||||
|
scanWifiNetworks();
|
||||||
|
rebuildScreen();
|
||||||
|
break;
|
||||||
|
case ACTION_WIFI_CLEAR:
|
||||||
|
clearWifiPrefs();
|
||||||
|
gWifiViewMode = WIFI_VIEW_OVERVIEW;
|
||||||
|
rebuildScreen();
|
||||||
|
break;
|
||||||
|
case ACTION_SERVER_EDIT_SOLANA:
|
||||||
|
openEditor(EDIT_CONTEXT_SOLANA_RPC,
|
||||||
|
SCREEN_SERVER,
|
||||||
|
"EDIT SOLANA RPC",
|
||||||
|
"Cursor starts at the end.",
|
||||||
|
gSolanaRpcUrl,
|
||||||
|
false);
|
||||||
|
break;
|
||||||
|
case ACTION_SERVER_EDIT_SHINE:
|
||||||
|
openEditor(EDIT_CONTEXT_SHINE_SERVER,
|
||||||
|
SCREEN_SERVER,
|
||||||
|
"EDIT SHINE HOST",
|
||||||
|
"Cursor starts at the end.",
|
||||||
|
gShineServerUrl,
|
||||||
|
false);
|
||||||
|
break;
|
||||||
|
case ACTION_EDITOR_SAVE:
|
||||||
|
applyEditorValue();
|
||||||
|
break;
|
||||||
|
case ACTION_EDITOR_CANCEL:
|
||||||
|
cancelEditor();
|
||||||
|
break;
|
||||||
case ACTION_NONE:
|
case ACTION_NONE:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -239,6 +575,33 @@ static lv_obj_t *makeButton(const char *text,
|
|||||||
return btn;
|
return btn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static lv_obj_t *makeKeyboardButton(const char *label,
|
||||||
|
const char *token,
|
||||||
|
lv_coord_t x,
|
||||||
|
lv_coord_t y,
|
||||||
|
lv_coord_t w,
|
||||||
|
lv_coord_t h,
|
||||||
|
uint32_t bgColor,
|
||||||
|
const lv_font_t *font = &lv_font_montserrat_18) {
|
||||||
|
lv_obj_t *btn = lv_btn_create(gRoot);
|
||||||
|
lv_obj_set_size(btn, w, h);
|
||||||
|
lv_obj_set_pos(btn, x, y);
|
||||||
|
lv_obj_set_style_radius(btn, 12, 0);
|
||||||
|
lv_obj_set_style_bg_color(btn, lv_color_hex(bgColor), 0);
|
||||||
|
lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_set_style_border_width(btn, 1, 0);
|
||||||
|
lv_obj_set_style_border_color(btn, lv_color_hex(0x6E8AA3), 0);
|
||||||
|
lv_obj_set_style_shadow_width(btn, 0, 0);
|
||||||
|
lv_obj_add_event_cb(btn, editorKeyCb, LV_EVENT_CLICKED, const_cast<char *>(token));
|
||||||
|
|
||||||
|
lv_obj_t *textObj = lv_label_create(btn);
|
||||||
|
lv_label_set_text(textObj, label);
|
||||||
|
lv_obj_set_style_text_font(textObj, font, 0);
|
||||||
|
lv_obj_set_style_text_color(textObj, lv_color_hex(0xFFFFFF), 0);
|
||||||
|
lv_obj_center(textObj);
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
static void makeTopIcons() {
|
static void makeTopIcons() {
|
||||||
lv_obj_t *icons = lv_label_create(gRoot);
|
lv_obj_t *icons = lv_label_create(gRoot);
|
||||||
lv_label_set_text(icons, "W BAT");
|
lv_label_set_text(icons, "W BAT");
|
||||||
@ -271,8 +634,10 @@ static void drawHome() {
|
|||||||
lv_obj_align_to(subserver, login, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 6);
|
lv_obj_align_to(subserver, login, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 6);
|
||||||
|
|
||||||
makeTopIcons();
|
makeTopIcons();
|
||||||
makeTitle("STATUS", 165, &lv_font_montserrat_28);
|
makeTitle("STATUS", 150, &lv_font_montserrat_28);
|
||||||
makeBody("Swipe left or tap the button below.", 214, 360);
|
showMessageAt(wifiHomeStatus(), 204);
|
||||||
|
showMessageAt(wifiSavedLabel(), 238);
|
||||||
|
makeBody("Swipe left or tap the button below.", 274, 360);
|
||||||
makeButton("SETTINGS", 22, 360, 436, 78, 0x2A6F97, ACTION_OPEN_SETTINGS, &lv_font_montserrat_24);
|
makeButton("SETTINGS", 22, 360, 436, 78, 0x2A6F97, ACTION_OPEN_SETTINGS, &lv_font_montserrat_24);
|
||||||
makeVersionTag();
|
makeVersionTag();
|
||||||
}
|
}
|
||||||
@ -295,7 +660,7 @@ static void drawSettingsMenu() {
|
|||||||
if (itemIndex == 2) action = ACTION_OPEN_ACCOUNT;
|
if (itemIndex == 2) action = ACTION_OPEN_ACCOUNT;
|
||||||
|
|
||||||
makeButton(kMenuItems[itemIndex], 22, 132 + visibleIndex * 126, 436, 104,
|
makeButton(kMenuItems[itemIndex], 22, 132 + visibleIndex * 126, 436, 104,
|
||||||
visibleIndex == 0 ? 0x355C7D : 0x2A9D8F, action, &lv_font_montserrat_24);
|
0x355C7D, action, &lv_font_montserrat_24);
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_t *hint = lv_label_create(gRoot);
|
lv_obj_t *hint = lv_label_create(gRoot);
|
||||||
@ -312,25 +677,189 @@ static void drawSettingsMenu() {
|
|||||||
makeVersionTag();
|
makeVersionTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawSimpleScreen(const char *title, const char *body) {
|
static lv_obj_t *makeNetworkButton(const char *text, int index, lv_coord_t y) {
|
||||||
|
lv_obj_t *btn = lv_btn_create(gRoot);
|
||||||
|
lv_obj_set_size(btn, 436, 52);
|
||||||
|
lv_obj_set_pos(btn, 22, y);
|
||||||
|
lv_obj_set_style_radius(btn, 14, 0);
|
||||||
|
lv_obj_set_style_bg_color(btn, lv_color_hex(0x2A6F97), 0);
|
||||||
|
lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_set_style_border_width(btn, 2, 0);
|
||||||
|
lv_obj_set_style_border_color(btn, lv_color_hex(0x6E8AA3), 0);
|
||||||
|
lv_obj_add_event_cb(btn, networkSelectCb, LV_EVENT_CLICKED, reinterpret_cast<void *>(static_cast<uintptr_t>(index)));
|
||||||
|
|
||||||
|
lv_obj_t *label = lv_label_create(btn);
|
||||||
|
lv_label_set_text(label, text);
|
||||||
|
lv_obj_set_width(label, 404);
|
||||||
|
lv_label_set_long_mode(label, LV_LABEL_LONG_DOT);
|
||||||
|
lv_obj_set_style_text_font(label, &lv_font_montserrat_18, 0);
|
||||||
|
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
|
||||||
|
lv_obj_center(label);
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawWifiOverview() {
|
||||||
|
makeTitle("Wi-Fi", 18, &lv_font_montserrat_24);
|
||||||
|
showMessageAt(wifiHomeStatus(), 60);
|
||||||
|
showMessageAt(wifiSavedLabel(), 92);
|
||||||
|
showMessageAt(gWifiStatusMessage, 124);
|
||||||
|
makeButton("SELECT NETWORK", 22, 182, 436, 68, 0x2A6F97, ACTION_WIFI_SELECT_NETWORK, &lv_font_montserrat_22);
|
||||||
|
makeButton("CLEAR SAVED WI-FI", 22, 270, 436, 68, 0x7D3A3A, ACTION_WIFI_CLEAR, &lv_font_montserrat_20);
|
||||||
|
makeButton("BACK", 140, 360, 200, 72, 0x5A6570, ACTION_BACK_SETTINGS, &lv_font_montserrat_22);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawWifiScanResults() {
|
||||||
|
makeTitle("SELECT NETWORK", 18, &lv_font_montserrat_24);
|
||||||
|
showMessageAt(gWifiStatusMessage, 58);
|
||||||
|
if (gScanResultCount == 0) {
|
||||||
|
showMessageAt("No networks available", 110);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < gScanResultCount; ++i) {
|
||||||
|
makeNetworkButton(gScanResults[i].c_str(), i, 100 + i * 58);
|
||||||
|
}
|
||||||
|
makeButton("SCAN AGAIN", 22, 370, 210, 64, 0x2A6F97, ACTION_WIFI_SELECT_NETWORK, &lv_font_montserrat_20);
|
||||||
|
makeButton("BACK", 248, 370, 210, 64, 0x5A6570, ACTION_BACK_SETTINGS, &lv_font_montserrat_20);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawWifiScreen() {
|
||||||
setRootStyle();
|
setRootStyle();
|
||||||
makeTitle(title, 56, &lv_font_montserrat_28);
|
|
||||||
makeBody(body, 132, 400);
|
if (gWifiViewMode == WIFI_VIEW_OVERVIEW) {
|
||||||
|
drawWifiOverview();
|
||||||
|
} else {
|
||||||
|
drawWifiScanResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
makeVersionTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawServerScreen() {
|
||||||
|
setRootStyle();
|
||||||
|
makeTitle("SERVER", 18, &lv_font_montserrat_24);
|
||||||
|
showMessageAt(gServerStatusMessage, 56);
|
||||||
|
showMessageAt(String("Solana: ") + gSolanaRpcUrl, 96);
|
||||||
|
makeButton("SOLANA RPC", 22, 146, 436, 84, 0x355C7D, ACTION_SERVER_EDIT_SOLANA, &lv_font_montserrat_24);
|
||||||
|
showMessageAt(String("Shine: ") + gShineServerUrl, 248);
|
||||||
|
makeButton("SHINE SERVER", 22, 298, 436, 84, 0x355C7D, ACTION_SERVER_EDIT_SHINE, &lv_font_montserrat_24);
|
||||||
|
makeBody("Swipe right to return to Settings.", 396, 420);
|
||||||
|
makeVersionTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawAccountScreen() {
|
||||||
|
setRootStyle();
|
||||||
|
makeTitle("ACCOUNT", 56, &lv_font_montserrat_28);
|
||||||
|
makeBody("Account screen", 132, 400);
|
||||||
makeBody("Swipe right to return to Settings.", 204, 400);
|
makeBody("Swipe right to return to Settings.", 204, 400);
|
||||||
makeButton("BACK", 140, 344, 200, 72, 0x5A6570, ACTION_BACK_SETTINGS, &lv_font_montserrat_22);
|
makeButton("BACK", 140, 344, 200, 72, 0x5A6570, ACTION_BACK_SETTINGS, &lv_font_montserrat_22);
|
||||||
makeVersionTag();
|
makeVersionTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawWifiScreen() {
|
static void drawKeyRow(const char *const *tokens,
|
||||||
drawSimpleScreen("Wi-Fi", "Wi-Fi screen");
|
int count,
|
||||||
|
lv_coord_t x,
|
||||||
|
lv_coord_t y,
|
||||||
|
lv_coord_t w,
|
||||||
|
lv_coord_t h,
|
||||||
|
lv_coord_t gap,
|
||||||
|
uint32_t bgColor,
|
||||||
|
bool uppercase = false,
|
||||||
|
const lv_font_t *font = &lv_font_montserrat_18) {
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
String label = tokens[i];
|
||||||
|
if (uppercase) {
|
||||||
|
label.toUpperCase();
|
||||||
|
}
|
||||||
|
makeKeyboardButton(label.c_str(), tokens[i], x + i * (w + gap), y, w, h, bgColor, font);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawServerScreen() {
|
static void drawTextEditorKeyboard(lv_coord_t originY) {
|
||||||
drawSimpleScreen("Server", "Server screen");
|
static const char *alphaLeftRow0[] = {"q", "w", "e", "r", "t"};
|
||||||
|
static const char *alphaLeftRow1[] = {"a", "s", "d", "f", "g"};
|
||||||
|
static const char *alphaLeftRow2[] = {"z", "x", "c", "v", "b"};
|
||||||
|
static const char *alphaRightRow0[] = {"y", "u", "i", "o", "p"};
|
||||||
|
static const char *alphaRightRow1[] = {"h", "j", "k", "l"};
|
||||||
|
static const char *alphaRightRow2[] = {"n", "m", ".", "-"};
|
||||||
|
|
||||||
|
static const char *symLeftRow0[] = {"1", "2", "3", "4", "5"};
|
||||||
|
static const char *symLeftRow1[] = {"://", "/", ":", "_", "@"};
|
||||||
|
static const char *symLeftRow2[] = {".com", ".net", ".org", ".me", ".io"};
|
||||||
|
static const char *symRightRow0[] = {"6", "7", "8", "9", "0"};
|
||||||
|
static const char *symRightRow1[] = {"?", "&", "=", "+", "-"};
|
||||||
|
static const char *symRightRow2[] = {"%", "#", "~", ":", "/"};
|
||||||
|
|
||||||
|
const lv_coord_t keyH = 50;
|
||||||
|
const lv_coord_t keyW5 = 84;
|
||||||
|
const lv_coord_t keyW4 = 104;
|
||||||
|
const lv_coord_t gap = 8;
|
||||||
|
const lv_coord_t row0Y = originY;
|
||||||
|
const lv_coord_t row1Y = originY + 58;
|
||||||
|
const lv_coord_t row2Y = originY + 116;
|
||||||
|
const lv_coord_t row3Y = originY + 180;
|
||||||
|
const bool uppercase = gKeyboardMode == KEYBOARD_MODE_ALPHA && gKeyboardShift;
|
||||||
|
|
||||||
|
if (gKeyboardMode == KEYBOARD_MODE_ALPHA) {
|
||||||
|
if (gKeyboardPage == 0) {
|
||||||
|
drawKeyRow(alphaLeftRow0, 5, 20, row0Y, keyW5, keyH, gap, 0x2A6F97, uppercase);
|
||||||
|
drawKeyRow(alphaLeftRow1, 5, 20, row1Y, keyW5, keyH, gap, 0x2A6F97, uppercase);
|
||||||
|
drawKeyRow(alphaLeftRow2, 5, 20, row2Y, keyW5, keyH, gap, 0x2A6F97, uppercase);
|
||||||
|
} else {
|
||||||
|
drawKeyRow(alphaRightRow0, 5, 20, row0Y, keyW5, keyH, gap, 0x2A6F97, uppercase);
|
||||||
|
drawKeyRow(alphaRightRow1, 4, 20, row1Y, keyW5, keyH, gap, 0x2A6F97, uppercase);
|
||||||
|
drawKeyRow(alphaRightRow2, 4, 20, row2Y, keyW5, keyH, gap, 0x2A6F97, uppercase);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (gKeyboardPage == 0) {
|
||||||
|
drawKeyRow(symLeftRow0, 5, 20, row0Y, keyW5, keyH, gap, 0x2A6F97);
|
||||||
|
drawKeyRow(symLeftRow1, 5, 20, row1Y, keyW5, keyH, gap, 0x2A6F97, false, &lv_font_montserrat_16);
|
||||||
|
drawKeyRow(symLeftRow2, 5, 20, row2Y, keyW5, keyH, gap, 0x2A6F97, false, &lv_font_montserrat_16);
|
||||||
|
} else {
|
||||||
|
drawKeyRow(symRightRow0, 5, 20, row0Y, keyW5, keyH, gap, 0x2A6F97);
|
||||||
|
drawKeyRow(symRightRow1, 5, 20, row1Y, keyW5, keyH, gap, 0x2A6F97, false, &lv_font_montserrat_16);
|
||||||
|
drawKeyRow(symRightRow2, 5, 20, row2Y, keyW5, keyH, gap, 0x2A6F97, false, &lv_font_montserrat_16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeKeyboardButton(gKeyboardMode == KEYBOARD_MODE_ALPHA ? "123" : "ABC", "<MODE>", 20, row3Y, 88, 54, 0x5C6F82, &lv_font_montserrat_18);
|
||||||
|
makeKeyboardButton(gKeyboardShift ? "SHIFT*" : "SHIFT", "<SHIFT>", 116, row3Y, 88, 54, 0x7A5C9B, &lv_font_montserrat_16);
|
||||||
|
makeKeyboardButton("DEL", "<DEL>", 212, row3Y, 76, 54, 0x7D3A3A, &lv_font_montserrat_18);
|
||||||
|
makeKeyboardButton("SAVE", "<SAVE>", 296, row3Y, 76, 54, 0x2A9D8F, &lv_font_montserrat_18);
|
||||||
|
makeKeyboardButton("CANCEL", "<CANCEL>", 380, row3Y, 80, 54, 0x5A6570, &lv_font_montserrat_16);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawAccountScreen() {
|
static void drawTextEditScreen() {
|
||||||
drawSimpleScreen("Account", "Account screen");
|
setRootStyle();
|
||||||
|
gInputTextArea = nullptr;
|
||||||
|
|
||||||
|
makeTitle(gEditTitle.c_str(), 8, &lv_font_montserrat_24);
|
||||||
|
|
||||||
|
gInputTextArea = lv_textarea_create(gRoot);
|
||||||
|
lv_obj_set_size(gInputTextArea, 436, 58);
|
||||||
|
lv_obj_set_pos(gInputTextArea, 22, 48);
|
||||||
|
lv_textarea_set_one_line(gInputTextArea, true);
|
||||||
|
lv_textarea_set_text(gInputTextArea, gEditValue.c_str());
|
||||||
|
lv_textarea_set_cursor_pos(gInputTextArea, LV_TEXTAREA_CURSOR_LAST);
|
||||||
|
lv_textarea_set_password_mode(gInputTextArea, gEditIsPassword);
|
||||||
|
lv_obj_set_style_text_font(gInputTextArea, &lv_font_montserrat_18, 0);
|
||||||
|
lv_obj_set_style_bg_color(gInputTextArea, lv_color_hex(0x132131), 0);
|
||||||
|
lv_obj_set_style_text_color(gInputTextArea, lv_color_hex(0xFFFFFF), 0);
|
||||||
|
lv_obj_set_style_border_color(gInputTextArea, lv_color_hex(0x6E8AA3), 0);
|
||||||
|
lv_obj_set_style_border_width(gInputTextArea, 2, 0);
|
||||||
|
lv_obj_add_state(gInputTextArea, LV_STATE_FOCUSED);
|
||||||
|
|
||||||
|
lv_obj_t *panel = lv_obj_create(gRoot);
|
||||||
|
lv_obj_set_size(panel, TEXT_EDIT_PANEL_W, TEXT_EDIT_PANEL_H);
|
||||||
|
lv_obj_set_pos(panel, TEXT_EDIT_PANEL_X, TEXT_EDIT_PANEL_Y);
|
||||||
|
lv_obj_set_style_radius(panel, 16, 0);
|
||||||
|
lv_obj_set_style_bg_color(panel, lv_color_hex(0x0F1A27), 0);
|
||||||
|
lv_obj_set_style_bg_opa(panel, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_set_style_border_width(panel, 1, 0);
|
||||||
|
lv_obj_set_style_border_color(panel, lv_color_hex(0x2E455A), 0);
|
||||||
|
lv_obj_clear_flag(panel, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
|
||||||
|
drawTextEditorKeyboard(TEXT_EDIT_PANEL_Y + 10);
|
||||||
|
|
||||||
|
makeVersionTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rebuildScreen() {
|
static void rebuildScreen() {
|
||||||
@ -356,6 +885,9 @@ static void rebuildScreen() {
|
|||||||
case SCREEN_ACCOUNT:
|
case SCREEN_ACCOUNT:
|
||||||
drawAccountScreen();
|
drawAccountScreen();
|
||||||
break;
|
break;
|
||||||
|
case SCREEN_TEXT_EDIT:
|
||||||
|
drawTextEditScreen();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,6 +928,7 @@ static void handleSettingsSwipe(SwipeDirection swipe) {
|
|||||||
|
|
||||||
static void handleWifiSwipe(SwipeDirection swipe) {
|
static void handleWifiSwipe(SwipeDirection swipe) {
|
||||||
if (swipe == SWIPE_RIGHT) {
|
if (swipe == SWIPE_RIGHT) {
|
||||||
|
gWifiViewMode = WIFI_VIEW_OVERVIEW;
|
||||||
showScreen(SCREEN_SETTINGS_MENU);
|
showScreen(SCREEN_SETTINGS_MENU);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -412,6 +945,26 @@ static void handleAccountSwipe(SwipeDirection swipe) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handleTextEditSwipe(SwipeDirection swipe) {
|
||||||
|
if (!isTextEditKeyboardSwipeArea(gTouchStartX, gTouchStartY)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (swipe == SWIPE_LEFT) {
|
||||||
|
gKeyboardPage = min(gKeyboardPage + 1, 1);
|
||||||
|
rebuildScreen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (swipe == SWIPE_RIGHT) {
|
||||||
|
gKeyboardPage = max(gKeyboardPage - 1, 0);
|
||||||
|
rebuildScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isTextEditKeyboardSwipeArea(int16_t x, int16_t y) {
|
||||||
|
return x >= TEXT_EDIT_PANEL_X && x <= (TEXT_EDIT_PANEL_X + TEXT_EDIT_PANEL_W) &&
|
||||||
|
y >= TEXT_EDIT_PANEL_Y && y <= (TEXT_EDIT_PANEL_Y + TEXT_EDIT_PANEL_H);
|
||||||
|
}
|
||||||
|
|
||||||
static void handleSwipe(SwipeDirection swipe) {
|
static void handleSwipe(SwipeDirection swipe) {
|
||||||
if (swipe == SWIPE_NONE) {
|
if (swipe == SWIPE_NONE) {
|
||||||
return;
|
return;
|
||||||
@ -433,6 +986,9 @@ static void handleSwipe(SwipeDirection swipe) {
|
|||||||
case SCREEN_ACCOUNT:
|
case SCREEN_ACCOUNT:
|
||||||
handleAccountSwipe(swipe);
|
handleAccountSwipe(swipe);
|
||||||
break;
|
break;
|
||||||
|
case SCREEN_TEXT_EDIT:
|
||||||
|
handleTextEditSwipe(swipe);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,6 +1007,10 @@ void setup() {
|
|||||||
gTouch.setSwapXY(true);
|
gTouch.setSwapXY(true);
|
||||||
gTouch.setMirrorXY(true, false);
|
gTouch.setMirrorXY(true, false);
|
||||||
|
|
||||||
|
gPrefs.begin("shine-nav-ui", false);
|
||||||
|
loadPrefs();
|
||||||
|
beginSavedWifi();
|
||||||
|
|
||||||
lv_init();
|
lv_init();
|
||||||
|
|
||||||
uint32_t screenWidth = gfx->width();
|
uint32_t screenWidth = gfx->width();
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
client.version=1.2.142
|
client.version=1.2.143
|
||||||
server.version=1.2.134
|
server.version=1.2.135
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user