Files
lvgl/src/widgets/keyboard/lv_keyboard.c
2024-10-09 13:48:18 +02:00

491 lines
18 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file lv_keyboard.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_keyboard_private.h"
#include "../../core/lv_obj_class_private.h"
#if LV_USE_KEYBOARD
#include "../textarea/lv_textarea.h"
#include "../../misc/lv_assert.h"
#include "../../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_keyboard_class)
#define LV_KB_BTN(width) LV_BUTTONMATRIX_CTRL_POPOVER | width
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_keyboard_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_keyboard_update_map(lv_obj_t * obj);
static void lv_keyboard_update_ctrl_map(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
#if LV_USE_OBJ_PROPERTY
static const lv_property_ops_t properties[] = {
{
.id = LV_PROPERTY_KEYBOARD_TEXTAREA,
.setter = lv_keyboard_set_textarea,
.getter = lv_keyboard_get_textarea,
},
{
.id = LV_PROPERTY_KEYBOARD_MODE,
.setter = lv_keyboard_set_mode,
.getter = lv_keyboard_get_mode,
},
{
.id = LV_PROPERTY_KEYBOARD_POPOVERS,
.setter = lv_keyboard_set_popovers,
.getter = lv_keyboard_get_popovers,
},
{
.id = LV_PROPERTY_KEYBOARD_SELECTED_BUTTON,
.setter = lv_buttonmatrix_set_selected_button,
.getter = lv_keyboard_get_selected_button,
},
};
#endif
const lv_obj_class_t lv_keyboard_class = {
.constructor_cb = lv_keyboard_constructor,
.width_def = LV_PCT(100),
.height_def = LV_PCT(50),
.instance_size = sizeof(lv_keyboard_t),
.editable = 1,
.base_class = &lv_buttonmatrix_class,
.name = "keyboard",
#if LV_USE_OBJ_PROPERTY
.prop_index_start = LV_PROPERTY_KEYBOARD_START,
.prop_index_end = LV_PROPERTY_KEYBOARD_END,
.properties = properties,
.properties_count = sizeof(properties) / sizeof(properties[0]),
#if LV_USE_OBJ_PROPERTY_NAME
.property_names = lv_keyboard_property_names,
.names_count = sizeof(lv_keyboard_property_names) / sizeof(lv_property_name_t),
#endif
#endif
};
static const char * const default_kb_map_lc[] = {"1#", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", LV_SYMBOL_BACKSPACE, "\n",
"ABC", "a", "s", "d", "f", "g", "h", "j", "k", "l", LV_SYMBOL_NEW_LINE, "\n",
"_", "-", "z", "x", "c", "v", "b", "n", "m", ".", ",", ":", "\n",
LV_SYMBOL_KEYBOARD,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
"أب",
#endif
LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_buttonmatrix_ctrl_t default_kb_ctrl_lc_map[] = {
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 5, LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_BUTTONMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 6, LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_BUTTONMATRIX_CTRL_CHECKED | 7,
LV_BUTTONMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BUTTONMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BUTTONMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BUTTONMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BUTTONMATRIX_CTRL_CHECKED | LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2,
#endif
LV_BUTTONMATRIX_CTRL_CHECKED | 2, 6, LV_BUTTONMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2
};
static const char * const default_kb_map_uc[] = {"1#", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", LV_SYMBOL_BACKSPACE, "\n",
"abc", "A", "S", "D", "F", "G", "H", "J", "K", "L", LV_SYMBOL_NEW_LINE, "\n",
"_", "-", "Z", "X", "C", "V", "B", "N", "M", ".", ",", ":", "\n",
LV_SYMBOL_CLOSE,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
"أب",
#endif
LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_buttonmatrix_ctrl_t default_kb_ctrl_uc_map[] = {
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 5, LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_BUTTONMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 6, LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_BUTTONMATRIX_CTRL_CHECKED | 7,
LV_BUTTONMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BUTTONMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BUTTONMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BUTTONMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BUTTONMATRIX_CTRL_CHECKED | LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2,
#endif
LV_BUTTONMATRIX_CTRL_CHECKED | 2, 6, LV_BUTTONMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2
};
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
static const char * const default_kb_map_ar[] = {
"1#", "ض", "ص", "ث", "ق", "ف", "غ", "ع", "ه", "خ", "ح", "ج", "\n",
"ش", "س", "ي", "ب", "ل", "ا", "ت", "ن", "م", "ك", "ط", LV_SYMBOL_BACKSPACE, "\n",
"ذ", "ء", "ؤ", "ر", "ى", "ة", "و", "ز", "ظ", "د", "ز", "ظ", "د", "\n",
LV_SYMBOL_CLOSE, "abc", LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_NEW_LINE, LV_SYMBOL_OK, ""
};
static const lv_buttonmatrix_ctrl_t default_kb_ctrl_ar_map[] = {
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2, LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2, 2, 6, 2, 3, LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2
};
#endif
static const char * const default_kb_map_spec[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", LV_SYMBOL_BACKSPACE, "\n",
"abc", "+", "&", "/", "*", "=", "%", "!", "?", "#", "<", ">", "\n",
"\\", "@", "$", "(", ")", "{", "}", "[", "]", ";", "\"", "'", "\n",
LV_SYMBOL_KEYBOARD,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
"أب",
#endif
LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_buttonmatrix_ctrl_t default_kb_ctrl_spec_map[] = {
LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BUTTONMATRIX_CTRL_CHECKED | 2,
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2, LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1),
LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2,
#endif
LV_BUTTONMATRIX_CTRL_CHECKED | 2, 6, LV_BUTTONMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2
};
static const char * const default_kb_map_num[] = {"1", "2", "3", LV_SYMBOL_KEYBOARD, "\n",
"4", "5", "6", LV_SYMBOL_OK, "\n",
"7", "8", "9", LV_SYMBOL_BACKSPACE, "\n",
"+/-", "0", ".", LV_SYMBOL_LEFT, LV_SYMBOL_RIGHT, ""
};
static const lv_buttonmatrix_ctrl_t default_kb_ctrl_num_map[] = {
1, 1, 1, LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2,
1, 1, 1, LV_KEYBOARD_CTRL_BUTTON_FLAGS | 2,
1, 1, 1, 2,
1, 1, 1, 1, 1
};
static const char * const * kb_map[10] = {
default_kb_map_lc,
default_kb_map_uc,
default_kb_map_spec,
default_kb_map_num,
default_kb_map_lc,
default_kb_map_lc,
default_kb_map_lc,
default_kb_map_lc,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
default_kb_map_ar,
#endif
NULL
};
static const lv_buttonmatrix_ctrl_t * kb_ctrl[10] = {
default_kb_ctrl_lc_map,
default_kb_ctrl_uc_map,
default_kb_ctrl_spec_map,
default_kb_ctrl_num_map,
default_kb_ctrl_lc_map,
default_kb_ctrl_lc_map,
default_kb_ctrl_lc_map,
default_kb_ctrl_lc_map,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
default_kb_ctrl_ar_map,
#endif
NULL
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_keyboard_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_keyboard_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
void lv_keyboard_set_textarea(lv_obj_t * obj, lv_obj_t * ta)
{
if(ta) {
LV_ASSERT_OBJ(ta, &lv_textarea_class);
}
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
/*Hide the cursor of the old Text area if cursor management is enabled*/
if(keyboard->ta) {
lv_obj_remove_state(obj, LV_STATE_FOCUSED);
}
keyboard->ta = ta;
/*Show the cursor of the new Text area if cursor management is enabled*/
if(keyboard->ta) {
lv_obj_add_state(obj, LV_STATE_FOCUSED);
}
}
void lv_keyboard_set_mode(lv_obj_t * obj, lv_keyboard_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if(keyboard->mode == mode) return;
keyboard->mode = mode;
lv_keyboard_update_map(obj);
}
void lv_keyboard_set_popovers(lv_obj_t * obj, bool en)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if(keyboard->popovers == en) {
return;
}
keyboard->popovers = en;
lv_keyboard_update_ctrl_map(obj);
}
void lv_keyboard_set_map(lv_obj_t * obj, lv_keyboard_mode_t mode, const char * const map[],
const lv_buttonmatrix_ctrl_t ctrl_map[])
{
LV_ASSERT_OBJ(obj, MY_CLASS);
kb_map[mode] = map;
kb_ctrl[mode] = ctrl_map;
lv_keyboard_update_map(obj);
}
/*=====================
* Getter functions
*====================*/
lv_obj_t * lv_keyboard_get_textarea(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->ta;
}
lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->mode;
}
bool lv_keyboard_get_popovers(const lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->popovers;
}
/*=====================
* Other functions
*====================*/
void lv_keyboard_def_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_current_target(e);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
uint32_t btn_id = lv_buttonmatrix_get_selected_button(obj);
if(btn_id == LV_BUTTONMATRIX_BUTTON_NONE) return;
const char * txt = lv_buttonmatrix_get_button_text(obj, btn_id);
if(txt == NULL) return;
if(lv_strcmp(txt, "abc") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
lv_buttonmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_TEXT_LOWER]);
lv_keyboard_update_ctrl_map(obj);
return;
}
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
else if(lv_strcmp(txt, "أب") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_TEXT_ARABIC;
lv_buttonmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_TEXT_ARABIC]);
lv_keyboard_update_ctrl_map(obj);
return;
}
#endif
else if(lv_strcmp(txt, "ABC") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_TEXT_UPPER;
lv_buttonmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_TEXT_UPPER]);
lv_keyboard_update_ctrl_map(obj);
return;
}
else if(lv_strcmp(txt, "1#") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_SPECIAL;
lv_buttonmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_SPECIAL]);
lv_keyboard_update_ctrl_map(obj);
return;
}
else if(lv_strcmp(txt, LV_SYMBOL_CLOSE) == 0 || lv_strcmp(txt, LV_SYMBOL_KEYBOARD) == 0) {
lv_result_t res = lv_obj_send_event(obj, LV_EVENT_CANCEL, NULL);
if(res != LV_RESULT_OK) return;
if(keyboard->ta) {
res = lv_obj_send_event(keyboard->ta, LV_EVENT_CANCEL, NULL);
if(res != LV_RESULT_OK) return;
}
return;
}
else if(lv_strcmp(txt, LV_SYMBOL_OK) == 0) {
lv_result_t res = lv_obj_send_event(obj, LV_EVENT_READY, NULL);
if(res != LV_RESULT_OK) return;
if(keyboard->ta) {
res = lv_obj_send_event(keyboard->ta, LV_EVENT_READY, NULL);
if(res != LV_RESULT_OK) return;
}
return;
}
/*Add the characters to the text area if set*/
if(keyboard->ta == NULL) return;
if(lv_strcmp(txt, "Enter") == 0 || lv_strcmp(txt, LV_SYMBOL_NEW_LINE) == 0) {
lv_textarea_add_char(keyboard->ta, '\n');
if(lv_textarea_get_one_line(keyboard->ta)) {
lv_result_t res = lv_obj_send_event(keyboard->ta, LV_EVENT_READY, NULL);
if(res != LV_RESULT_OK) return;
}
}
else if(lv_strcmp(txt, LV_SYMBOL_LEFT) == 0) {
lv_textarea_cursor_left(keyboard->ta);
}
else if(lv_strcmp(txt, LV_SYMBOL_RIGHT) == 0) {
lv_textarea_cursor_right(keyboard->ta);
}
else if(lv_strcmp(txt, LV_SYMBOL_BACKSPACE) == 0) {
lv_textarea_delete_char(keyboard->ta);
}
else if(lv_strcmp(txt, "+/-") == 0) {
uint32_t cur = lv_textarea_get_cursor_pos(keyboard->ta);
const char * ta_txt = lv_textarea_get_text(keyboard->ta);
if(ta_txt[0] == '-') {
lv_textarea_set_cursor_pos(keyboard->ta, 1);
lv_textarea_delete_char(keyboard->ta);
lv_textarea_add_char(keyboard->ta, '+');
lv_textarea_set_cursor_pos(keyboard->ta, cur);
}
else if(ta_txt[0] == '+') {
lv_textarea_set_cursor_pos(keyboard->ta, 1);
lv_textarea_delete_char(keyboard->ta);
lv_textarea_add_char(keyboard->ta, '-');
lv_textarea_set_cursor_pos(keyboard->ta, cur);
}
else {
lv_textarea_set_cursor_pos(keyboard->ta, 0);
lv_textarea_add_char(keyboard->ta, '-');
lv_textarea_set_cursor_pos(keyboard->ta, cur + 1);
}
}
else {
lv_textarea_add_text(keyboard->ta, txt);
}
}
const char * const * lv_keyboard_get_map_array(const lv_obj_t * kb)
{
return lv_buttonmatrix_get_map(kb);
}
uint32_t lv_keyboard_get_selected_button(const lv_obj_t * obj)
{
return lv_buttonmatrix_get_selected_button(obj);
}
const char * lv_keyboard_get_button_text(const lv_obj_t * obj, uint32_t btn_id)
{
return lv_buttonmatrix_get_button_text(obj, btn_id);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_keyboard_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_remove_flag(obj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
keyboard->ta = NULL;
keyboard->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
keyboard->popovers = 0;
lv_obj_align(obj, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_add_event_cb(obj, lv_keyboard_def_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_set_style_base_dir(obj, LV_BASE_DIR_LTR, 0);
lv_keyboard_update_map(obj);
}
/**
* Update the key and control map for the current mode
* @param obj pointer to a keyboard object
*/
static void lv_keyboard_update_map(lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
lv_buttonmatrix_set_map(obj, kb_map[keyboard->mode]);
lv_keyboard_update_ctrl_map(obj);
}
/**
* Update the control map for the current mode
* @param obj pointer to a keyboard object
*/
static void lv_keyboard_update_ctrl_map(lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if(keyboard->popovers) {
/*Apply the current control map (already includes LV_BUTTONMATRIX_CTRL_POPOVER flags)*/
lv_buttonmatrix_set_ctrl_map(obj, kb_ctrl[keyboard->mode]);
}
else {
/*Make a copy of the current control map*/
lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj;
lv_buttonmatrix_ctrl_t * ctrl_map = lv_malloc(btnm->btn_cnt * sizeof(lv_buttonmatrix_ctrl_t));
lv_memcpy(ctrl_map, kb_ctrl[keyboard->mode], sizeof(lv_buttonmatrix_ctrl_t) * btnm->btn_cnt);
/*Remove all LV_BUTTONMATRIX_CTRL_POPOVER flags*/
uint32_t i;
for(i = 0; i < btnm->btn_cnt; i++) {
ctrl_map[i] &= (~LV_BUTTONMATRIX_CTRL_POPOVER);
}
/*Apply new control map and clean up*/
lv_buttonmatrix_set_ctrl_map(obj, ctrl_map);
lv_free(ctrl_map);
}
}
#endif /*LV_USE_KEYBOARD*/