⌨️
【Wio Terminal/LvGL】Virtual keyboard
で仮想キーボード
引用:https://docs.lvgl.io/7.11/widgets/keyboard.html#keyboard-with-text-area
画像のようなキーボードを作ることができる。画面上部は、キーボードの入力内容が表示されるテキストエリアである。
プログラム
simple_keyboard.ino
#include <lvgl.h>
#include <TFT_eSPI.h>
/* tick period */
constexpr uint16_t TICK_PERIOD = 5;
/* TFT LCD */
static TFT_eSPI TFT;
/* display buffer */
static lv_disp_buf_t DISP_BUF;
/* color buffer */
static lv_color_t COLOR_BUF[LV_HOR_RES_MAX * 10];
/* input device */
static lv_indev_t* INPUT_DEVICE = nullptr;
/* keyboard group */
static lv_group_t* KEYBOARD_GROUP = nullptr;
/* keyboard */
static lv_obj_t* KEYBOARD = nullptr;
/* textarea */
static lv_obj_t* TEXTAREA = nullptr;
/* 画面初期化 */
void display_flush(
lv_disp_drv_t* disp,
const lv_area_t* area,
lv_color_t* color_p
);
/* Reading input device (嘘) */
bool read_encoder(
lv_indev_drv_t* indev,
lv_indev_data_t* data
);
/* system tick */
static void tick_handler();
/* 設定 */
void backend_setup();
/* keyboard event callback */
static void keyboard_event_cb(
lv_obj_t* keyboard,
lv_event_t e
);
/* textarea event callback */
static void textarea_event_cb(
lv_obj_t* ta_local,
lv_event_t e
);
/* set keyboard */
static void set_keyboard();
/* set textarea */
static void set_textarea();
/* 画面要素 */
void set_contents();
/* 5way switch */
void wio_joy_handler();
void set_keyboard() {
KEYBOARD = lv_keyboard_create(lv_scr_act(), NULL);
lv_keyboard_set_cursor_manage(KEYBOARD, true);
lv_obj_set_event_cb(KEYBOARD, keyboard_event_cb);
lv_keyboard_set_textarea(KEYBOARD, TEXTAREA);
lv_group_add_obj(KEYBOARD_GROUP, KEYBOARD);
}
void set_textarea() {
/*Create a text area. The keyboard will write here*/
TEXTAREA = lv_textarea_create(lv_scr_act(), NULL);
lv_obj_align(TEXTAREA, NULL, LV_ALIGN_IN_TOP_MID, 0, LV_DPI / 16);
lv_obj_set_event_cb(TEXTAREA, textarea_event_cb);
lv_textarea_set_text(TEXTAREA, "");
lv_coord_t max_h = LV_VER_RES / 2 - LV_DPI / 8;
if (lv_obj_get_height(TEXTAREA) > max_h)
lv_obj_set_height(TEXTAREA, max_h);
}
void set_contents() {
KEYBOARD_GROUP = lv_group_create();
lv_indev_set_group(INPUT_DEVICE, KEYBOARD_GROUP);
set_textarea();
set_keyboard();
}
void wio_joy_handler() {
/*
↑: LV_KEY_UP
↓: LV_KEY_DOWN
←: LV_KEY_LEFT
→: LV_KEY_RIGHT
PRESS:
keyboard有効時: LV_KEY_ENTER
keyboard無効時: LV_EVENT_CLICKED
*/
if (digitalRead(WIO_5S_UP) == LOW) {
/* groupに入力LV_KEY_UPを送る */
if (KEYBOARD != nullptr) {
lv_group_send_data(KEYBOARD_GROUP, LV_KEY_UP);
}
}
else if (digitalRead(WIO_5S_DOWN) == LOW) {
/* groupに入力LV_KEY_DOWNを送る */
if (KEYBOARD != nullptr) {
lv_group_send_data(KEYBOARD_GROUP, LV_KEY_DOWN);
}
}
else if (digitalRead(WIO_5S_LEFT) == LOW) {
/* groupに入力LV_KEY_LEFTを送る */
if (KEYBOARD != nullptr) {
lv_group_send_data(KEYBOARD_GROUP, LV_KEY_LEFT);
}
}
else if (digitalRead(WIO_5S_RIGHT) == LOW) {
/* groupに入力LV_KEY_RIGHTを送る */
if (KEYBOARD != nullptr) {
lv_group_send_data(KEYBOARD_GROUP, LV_KEY_RIGHT);
}
}
else if (digitalRead(WIO_5S_PRESS) == LOW) {
if (KEYBOARD != nullptr) {
/* groupに入力LV_KEY_ENTERを送る */
lv_group_send_data(KEYBOARD_GROUP, LV_KEY_ENTER);
lv_event_send(KEYBOARD, LV_EVENT_VALUE_CHANGED, NULL);
}
else {
/* LV_EVENT_CLICKEDを起こす */
lv_event_send(TEXTAREA, LV_EVENT_CLICKED, NULL);
}
}
else {
return;
}
delay(200);
}
void setup() {
/* 設定 */
backend_setup();
/* 画面構成 */
set_contents();
}
void loop() {
tick_handler();
lv_task_handler();
wio_joy_handler();
delay(TICK_PERIOD);
}
simple_keyboard_settings.ino
/*
画面初期化
画面いっぱい色で塗り潰している
*/
void display_flush(
lv_disp_drv_t* disp,
const lv_area_t* area,
lv_color_t* color_p
) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
TFT.startWrite();
TFT.setAddrWindow(area->x1, area->y1, w, h);
TFT.pushColors(&color_p->full, w * h, true);
TFT.endWrite();
/* callbackではこれを召喚しなければならない */
lv_disp_flush_ready(disp);
}
/* Reading input device (嘘) */
bool read_encoder(
lv_indev_drv_t* indev,
lv_indev_data_t* data
) {
/* no input */
return false;
}
/* system tick */
void tick_handler() {
lv_tick_inc(TICK_PERIOD);
}
/* keyboard event callback */
void keyboard_event_cb(lv_obj_t* keyboard, lv_event_t e) {
/* default event callback */
lv_keyboard_def_event_cb(KEYBOARD, e);
switch (e) {
case LV_EVENT_CANCEL:
/* textarea連携解除 */
lv_keyboard_set_textarea(KEYBOARD, NULL);
/* グループ退去 */
lv_group_remove_obj(KEYBOARD);
/* 削除 */
lv_obj_del(KEYBOARD);
KEYBOARD = nullptr;
/* clear all text */
lv_textarea_set_text(TEXTAREA, "");
break;
case LV_EVENT_APPLY:
/* textarea連携解除 */
lv_keyboard_set_textarea(KEYBOARD, NULL);
/* グループ退去 */
lv_group_remove_obj(KEYBOARD);
/* 削除 */
lv_obj_del(KEYBOARD);
KEYBOARD = nullptr;
break;
default:
break;
}
}
/* textarea event callback */
void textarea_event_cb(lv_obj_t* ta_local, lv_event_t e) {
if (e == LV_EVENT_CLICKED && KEYBOARD == nullptr) {
set_keyboard();
}
}
/* 設定 */
void backend_setup() {
/* lvglの初期化 */
lv_init();
/* TFTの初期設定 */
TFT.begin();
TFT.setRotation(3);
/* 画面表示用bufferそのものの初期化 */
lv_disp_buf_init(&DISP_BUF, COLOR_BUF, NULL, LV_HOR_RES_MAX * 10);
/* 画面制御設定 */
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = LV_HOR_RES_MAX;
disp_drv.ver_res = LV_VER_RES_MAX;
disp_drv.flush_cb = display_flush;
disp_drv.buffer = &DISP_BUF;
lv_disp_t* pDisplay = lv_disp_drv_register(&disp_drv);
/* 入力の設定 */
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_ENCODER;
indev_drv.read_cb = read_encoder;
INPUT_DEVICE = lv_indev_drv_register(&indev_drv);
/* enable 5way switch */
pinMode(WIO_5S_UP, INPUT);
pinMode(WIO_5S_DOWN, INPUT);
pinMode(WIO_5S_LEFT, INPUT);
pinMode(WIO_5S_RIGHT, INPUT);
pinMode(WIO_5S_PRESS, INPUT);
}
概説
簡単に説明を付す。
テキストエリア
static lv_obj_t* TEXTAREA = nullptr;
/* テキストエリアを作る */
void set_textarea() {
TEXTAREA = lv_textarea_create(lv_scr_act(), NULL);
/* 配置 */
lv_obj_align(TEXTAREA, NULL, LV_ALIGN_IN_TOP_MID, 0, LV_DPI / 16);
/* callbackを設定する */
lv_obj_set_event_cb(TEXTAREA, textarea_event_cb);
/* テキストを初期化する */
lv_textarea_set_text(TEXTAREA, "");
/* 高さ */
lv_coord_t max_h = LV_VER_RES / 2 - LV_DPI / 8;
if (lv_obj_get_height(TEXTAREA) > max_h)
lv_obj_set_height(TEXTAREA, max_h);
}
void textarea_event_cb(lv_obj_t* ta_local, lv_event_t e) {
/* キーボードが消えている場合 */
if (e == LV_EVENT_CLICKED && KEYBOARD == nullptr) {
/* キーボードを作る */
set_keyboard();
}
}
テキストエリアは作るだけで良い。
キーボード
static lv_obj_t* KEYBOARD = nullptr;
static lv_group_t* KEYBOARD_GROUP = nullptr;
/* キーボードを作る */
void set_keyboard() {
KEYBOARD = lv_keyboard_create(lv_scr_act(), NULL);
/* カーソルを有効にする */
lv_keyboard_set_cursor_manage(KEYBOARD, true);
/* callbackを設定する */
lv_obj_set_event_cb(KEYBOARD, keyboard_event_cb);
/* テキストエリアを連携する */
lv_keyboard_set_textarea(KEYBOARD, TEXTAREA);
/* グループに追加する */
lv_group_add_obj(KEYBOARD_GROUP, KEYBOARD);
}
/* キーボードのボタンが押された時の処理 */
void keyboard_event_cb(lv_obj_t* keyboard, lv_event_t e) {
/* 通常のボタンに対する処理は既定のものを使う */
lv_keyboard_def_event_cb(KEYBOARD, e);
/* それ以外のボタン */
switch (e) {
/* ✕ボタンを押した時 */
case LV_EVENT_CANCEL:
/* textarea連携解除 */
lv_keyboard_set_textarea(KEYBOARD, NULL);
/* グループ退去 */
lv_group_remove_obj(KEYBOARD);
/* 削除 */
lv_obj_del(KEYBOARD);
KEYBOARD = nullptr;
/* clear all text */
lv_textarea_set_text(TEXTAREA, "");
break;
/* ✓ボタンを押した時 */
case LV_EVENT_APPLY:
/* textarea連携解除 */
lv_keyboard_set_textarea(KEYBOARD, NULL);
/* グループ退去 */
lv_group_remove_obj(KEYBOARD);
/* 削除 */
lv_obj_del(KEYBOARD);
KEYBOARD = nullptr;
break;
default:
break;
}
}
キーボードを作る際、テキストエリアを連携する。
lv_keyboard_set_textarea(KEYBOARD, TEXTAREA);
キーボードに対して何らかの方法で入力するため、グループに追加しておく必要がある。
lv_group_add_obj(KEYBOARD_GROUP, KEYBOARD);
キー入力やテキストエリアへの出力は、lv_keyboard_def_event_cb()
で既に定義されている。
lv_keyboard_def_event_cb(KEYBOARD, e);
これを書いておけば、キー入力した際の動作は全て実装される。更に、連携したテキストエリアが有る場合、入力した文字がテキストエリアに出力される。
✕ボタンと✓ボタンに関する処理のみ、独自に定義している。どちらもキーボードが消える。✕ボタンを押した場合、テキストエリアの内容も消える。
スイッチの設定
void backend_setup() {
︙
/* enable 5way switch */
pinMode(WIO_5S_UP, INPUT);
pinMode(WIO_5S_DOWN, INPUT);
pinMode(WIO_5S_LEFT, INPUT);
pinMode(WIO_5S_RIGHT, INPUT);
pinMode(WIO_5S_PRESS, INPUT);
}
void wio_joy_handler() {
/*
↑: LV_KEY_UP
↓: LV_KEY_DOWN
←: LV_KEY_LEFT
→: LV_KEY_RIGHT
PRESS:
keyboard有効時: LV_KEY_ENTER
keyboard無効時: LV_EVENT_CLICKED
*/
if (digitalRead(WIO_5S_UP) == LOW) {
/* groupに入力LV_KEY_UPを送る */
if (KEYBOARD != nullptr) {
lv_group_send_data(KEYBOARD_GROUP, LV_KEY_UP);
}
}
else if (digitalRead(WIO_5S_DOWN) == LOW) {
/* groupに入力LV_KEY_DOWNを送る */
if (KEYBOARD != nullptr) {
lv_group_send_data(KEYBOARD_GROUP, LV_KEY_DOWN);
}
}
else if (digitalRead(WIO_5S_LEFT) == LOW) {
/* groupに入力LV_KEY_LEFTを送る */
if (KEYBOARD != nullptr) {
lv_group_send_data(KEYBOARD_GROUP, LV_KEY_LEFT);
}
}
else if (digitalRead(WIO_5S_RIGHT) == LOW) {
/* groupに入力LV_KEY_RIGHTを送る */
if (KEYBOARD != nullptr) {
lv_group_send_data(KEYBOARD_GROUP, LV_KEY_RIGHT);
}
}
else if (digitalRead(WIO_5S_PRESS) == LOW) {
if (KEYBOARD != nullptr) {
/* groupに入力LV_KEY_ENTERを送る */
lv_group_send_data(KEYBOARD_GROUP, LV_KEY_ENTER);
lv_event_send(KEYBOARD, LV_EVENT_VALUE_CHANGED, NULL);
}
else {
/* LV_EVENT_CLICKEDを起こす */
lv_event_send(TEXTAREA, LV_EVENT_CLICKED, NULL);
}
}
else {
return;
}
delay(200);
}
なおキーボードが無い時は、押下することでテキストエリアにLV_EVENT_CLICKED
を送る。テキストエリアの
void textarea_event_cb(lv_obj_t* ta_local, lv_event_t e) {
/* キーボードが消えている場合 */
if (e == LV_EVENT_CLICKED && KEYBOARD == nullptr) {
/* キーボードを作る */
set_keyboard();
}
}
Discussion