This repository has been archived on 2023-07-17. You can view files and clone it, but cannot push or open issues or pull requests.
bl_mcu_sdk/components/lvgl/lv_widgets/lv_keyboard.c
2021-06-20 12:25:46 +08:00

577 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_kb.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_keyboard.h"
#if LV_USE_KEYBOARD != 0
#include "../lv_misc/lv_debug.h"
#include "../lv_themes/lv_theme.h"
#include "lv_textarea.h"
/*********************
* DEFINES
*********************/
#define LV_OBJX_NAME "lv_keyboard"
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_keyboard_signal(lv_obj_t *kb, lv_signal_t sign, void *param);
static void lv_keyboard_update_map(lv_obj_t *kb);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_cb_t ancestor_signal;
/* clang-format off */
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_CLOSE,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
"أب",
#endif
LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_lc_map[] =
{
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
#endif
2, 6, 2, LV_KEYBOARD_CTRL_BTN_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_btnmatrix_ctrl_t default_kb_ctrl_uc_map[] =
{
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
#endif
2, 6, 2, LV_KEYBOARD_CTRL_BTN_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_btnmatrix_ctrl_t default_kb_ctrl_ar_map[] =
{
LV_KEYBOARD_CTRL_BTN_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_BTN_FLAGS | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2, 2, 6, 2, 3, LV_KEYBOARD_CTRL_BTN_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_CLOSE,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
"أب",
#endif
LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_spec_map[] =
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
#endif
2, 6, 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
static const char *const default_kb_map_num[] = {"1", "2", "3", LV_SYMBOL_CLOSE, "\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_btnmatrix_ctrl_t default_kb_ctrl_num_map[] =
{
1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
1, 1, 1, 2,
1, 1, 1, 1, 1
};
/* clang-format on */
static const char **kb_map[] = {
(const char **)default_kb_map_lc,
(const char **)default_kb_map_uc,
(const char **)default_kb_map_spec,
(const char **)default_kb_map_num
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
,
(const char **)default_kb_map_ar
#endif
};
static const lv_btnmatrix_ctrl_t *kb_ctrl[] = {
default_kb_ctrl_lc_map,
default_kb_ctrl_uc_map,
default_kb_ctrl_spec_map,
default_kb_ctrl_num_map
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
,
default_kb_ctrl_ar_map
#endif
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a keyboard objects
* @param par pointer to an object, it will be the parent of the new keyboard
* @param copy pointer to a keyboard object, if not NULL then the new object will be copied from it
* @return pointer to the created keyboard
*/
lv_obj_t *lv_keyboard_create(lv_obj_t *par, const lv_obj_t *copy)
{
LV_LOG_TRACE("keyboard create started");
/*Create the ancestor of keyboard*/
lv_obj_t *kb = lv_btnmatrix_create(par, copy);
LV_ASSERT_MEM(kb);
if (kb == NULL) {
return NULL;
}
if (ancestor_signal == NULL) {
ancestor_signal = lv_obj_get_signal_cb(kb);
}
/*Allocate the keyboard type specific extended data*/
lv_keyboard_ext_t *ext = lv_obj_allocate_ext_attr(kb, sizeof(lv_keyboard_ext_t));
LV_ASSERT_MEM(ext);
if (ext == NULL) {
lv_obj_del(kb);
return NULL;
}
/*Initialize the allocated 'ext' */
ext->ta = NULL;
ext->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
ext->cursor_mng = 0;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_cb(kb, lv_keyboard_signal);
/*Init the new keyboard keyboard*/
if (copy == NULL) {
/* Set a size which fits into the parent.
* Don't use `par` directly because if the window is created on a page it is moved to the
* scrollable so the parent has changed */
lv_obj_set_size(kb, lv_obj_get_width_fit(lv_obj_get_parent(kb)),
lv_obj_get_height_fit(lv_obj_get_parent(kb)) / 2);
lv_obj_align(kb, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
lv_obj_set_event_cb(kb, lv_keyboard_def_event_cb);
lv_obj_set_base_dir(kb, LV_BIDI_DIR_LTR);
lv_obj_add_protect(kb, LV_PROTECT_CLICK_FOCUS);
lv_btnmatrix_set_map(kb, kb_map[ext->mode]);
lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[ext->mode]);
lv_theme_apply(kb, LV_THEME_KEYBOARD);
}
/*Copy an existing keyboard*/
else {
lv_keyboard_ext_t *copy_ext = lv_obj_get_ext_attr(copy);
ext->ta = copy_ext->ta;
ext->mode = copy_ext->mode;
ext->cursor_mng = copy_ext->cursor_mng;
lv_btnmatrix_set_map(kb, kb_map[ext->mode]);
lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[ext->mode]);
/*Refresh the style with new signal function*/
// lv_obj_refresh_style(new_kb);
}
LV_LOG_INFO("keyboard created");
return kb;
}
/*=====================
* Setter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @param ta pointer to a Text Area object to write there
*/
void lv_keyboard_set_textarea(lv_obj_t *kb, lv_obj_t *ta)
{
LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
if (ta) {
LV_ASSERT_OBJ(ta, "lv_textarea");
}
lv_keyboard_ext_t *ext = lv_obj_get_ext_attr(kb);
/*Hide the cursor of the old Text area if cursor management is enabled*/
if (ext->ta && ext->cursor_mng) {
lv_textarea_set_cursor_hidden(ext->ta, true);
}
ext->ta = ta;
/*Show the cursor of the new Text area if cursor management is enabled*/
if (ext->ta && ext->cursor_mng) {
lv_textarea_set_cursor_hidden(ext->ta, false);
}
}
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @param mode the mode from 'lv_keyboard_mode_t'
*/
void lv_keyboard_set_mode(lv_obj_t *kb, lv_keyboard_mode_t mode)
{
LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
lv_keyboard_ext_t *ext = lv_obj_get_ext_attr(kb);
if (ext->mode == mode) {
return;
}
ext->mode = mode;
lv_btnmatrix_set_map(kb, kb_map[mode]);
lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[mode]);
}
/**
* Automatically hide or show the cursor of Text Area
* @param kb pointer to a Keyboard object
* @param en true: show cursor on the current text area, false: hide cursor
*/
void lv_keyboard_set_cursor_manage(lv_obj_t *kb, bool en)
{
LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
lv_keyboard_ext_t *ext = lv_obj_get_ext_attr(kb);
if (ext->cursor_mng == en) {
return;
}
ext->cursor_mng = en == false ? 0 : 1;
if (ext->ta) {
if (ext->cursor_mng) {
lv_textarea_set_cursor_hidden(ext->ta, false);
} else {
lv_textarea_set_cursor_hidden(ext->ta, true);
}
}
}
/**
* Set a new map for the keyboard
* @param kb pointer to a Keyboard object
* @param mode keyboard map to alter 'lv_keyboard_mode_t'
* @param map pointer to a string array to describe the map.
* See 'lv_btnmatrix_set_map()' for more info.
*/
void lv_keyboard_set_map(lv_obj_t *kb, lv_keyboard_mode_t mode, const char *map[])
{
kb_map[mode] = map;
lv_keyboard_update_map(kb);
}
/**
* Set the button control map (hidden, disabled etc.) for the keyboard. The
* control map array will be copied and so may be deallocated after this
* function returns.
* @param kb pointer to a keyboard object
* @param mode keyboard ctrl map to alter 'lv_keyboard_mode_t'
* @param ctrl_map pointer to an array of `lv_btn_ctrl_t` control bytes.
* See: `lv_btnmatrix_set_ctrl_map` for more details.
*/
void lv_keyboard_set_ctrl_map(lv_obj_t *kb, lv_keyboard_mode_t mode, const lv_btnmatrix_ctrl_t ctrl_map[])
{
kb_ctrl[mode] = ctrl_map;
lv_keyboard_update_map(kb);
}
/*=====================
* Getter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @return pointer to the assigned Text Area object
*/
lv_obj_t *lv_keyboard_get_textarea(const lv_obj_t *kb)
{
LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
lv_keyboard_ext_t *ext = lv_obj_get_ext_attr(kb);
return ext->ta;
}
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @return the current mode from 'lv_keyboard_mode_t'
*/
lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t *kb)
{
LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
lv_keyboard_ext_t *ext = lv_obj_get_ext_attr(kb);
return ext->mode;
}
/**
* Get the current cursor manage mode.
* @param kb pointer to a Keyboard object
* @return true: show cursor on the current text area, false: hide cursor
*/
bool lv_keyboard_get_cursor_manage(const lv_obj_t *kb)
{
LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
lv_keyboard_ext_t *ext = lv_obj_get_ext_attr(kb);
return ext->cursor_mng == 0 ? false : true;
}
/*=====================
* Other functions
*====================*/
/**
* Default keyboard event to add characters to the Text area and change the map.
* If a custom `event_cb` is added to the keyboard this function be called from it to handle the
* button clicks
* @param kb pointer to a keyboard
* @param event the triggering event
*/
void lv_keyboard_def_event_cb(lv_obj_t *kb, lv_event_t event)
{
LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
if (event != LV_EVENT_VALUE_CHANGED) {
return;
}
lv_keyboard_ext_t *ext = lv_obj_get_ext_attr(kb);
uint16_t btn_id = lv_btnmatrix_get_active_btn(kb);
if (btn_id == LV_BTNMATRIX_BTN_NONE) {
return;
}
if (lv_btnmatrix_get_btn_ctrl(kb, btn_id, LV_BTNMATRIX_CTRL_HIDDEN | LV_BTNMATRIX_CTRL_DISABLED)) {
return;
}
if (lv_btnmatrix_get_btn_ctrl(kb, btn_id, LV_BTNMATRIX_CTRL_NO_REPEAT) && event == LV_EVENT_LONG_PRESSED_REPEAT) {
return;
}
const char *txt = lv_btnmatrix_get_active_btn_text(kb);
if (txt == NULL) {
return;
}
/*Do the corresponding action according to the text of the button*/
if (strcmp(txt, "abc") == 0) {
ext->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
lv_btnmatrix_set_map(kb, kb_map[LV_KEYBOARD_MODE_TEXT_LOWER]);
lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[LV_KEYBOARD_MODE_TEXT_LOWER]);
return;
}
#if LV_USE_ARABIC_PERSIAN_CHARS == 1
else if (strcmp(txt, "أب") == 0) {
ext->mode = LV_KEYBOARD_MODE_TEXT_ARABIC;
lv_btnmatrix_set_map(kb, kb_map[LV_KEYBOARD_MODE_TEXT_ARABIC]);
lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[LV_KEYBOARD_MODE_TEXT_ARABIC]);
return;
}
#endif
else if (strcmp(txt, "ABC") == 0) {
ext->mode = LV_KEYBOARD_MODE_TEXT_UPPER;
lv_btnmatrix_set_map(kb, kb_map[LV_KEYBOARD_MODE_TEXT_UPPER]);
lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[LV_KEYBOARD_MODE_TEXT_UPPER]);
return;
} else if (strcmp(txt, "1#") == 0) {
ext->mode = LV_KEYBOARD_MODE_SPECIAL;
lv_btnmatrix_set_map(kb, kb_map[LV_KEYBOARD_MODE_SPECIAL]);
lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[LV_KEYBOARD_MODE_SPECIAL]);
return;
} else if (strcmp(txt, LV_SYMBOL_CLOSE) == 0) {
if (kb->event_cb != lv_keyboard_def_event_cb) {
lv_res_t res = lv_event_send(kb, LV_EVENT_CANCEL, NULL);
if (res != LV_RES_OK) {
return;
}
} else {
lv_keyboard_set_textarea(kb, NULL); /*De-assign the text area to hide it cursor if needed*/
lv_obj_del(kb);
return;
}
return;
} else if (strcmp(txt, LV_SYMBOL_OK) == 0) {
if (kb->event_cb != lv_keyboard_def_event_cb) {
lv_res_t res = lv_event_send(kb, LV_EVENT_APPLY, NULL);
if (res != LV_RES_OK) {
return;
}
} else {
lv_keyboard_set_textarea(kb, NULL); /*De-assign the text area to hide it cursor if needed*/
}
return;
}
/*Add the characters to the text area if set*/
if (ext->ta == NULL) {
return;
}
if (strcmp(txt, "Enter") == 0 || strcmp(txt, LV_SYMBOL_NEW_LINE) == 0) {
lv_textarea_add_char(ext->ta, '\n');
} else if (strcmp(txt, LV_SYMBOL_LEFT) == 0) {
lv_textarea_cursor_left(ext->ta);
} else if (strcmp(txt, LV_SYMBOL_RIGHT) == 0) {
lv_textarea_cursor_right(ext->ta);
} else if (strcmp(txt, LV_SYMBOL_BACKSPACE) == 0) {
lv_textarea_del_char(ext->ta);
} else if (strcmp(txt, "+/-") == 0) {
uint16_t cur = lv_textarea_get_cursor_pos(ext->ta);
const char *ta_txt = lv_textarea_get_text(ext->ta);
if (ta_txt[0] == '-') {
lv_textarea_set_cursor_pos(ext->ta, 1);
lv_textarea_del_char(ext->ta);
lv_textarea_add_char(ext->ta, '+');
lv_textarea_set_cursor_pos(ext->ta, cur);
} else if (ta_txt[0] == '+') {
lv_textarea_set_cursor_pos(ext->ta, 1);
lv_textarea_del_char(ext->ta);
lv_textarea_add_char(ext->ta, '-');
lv_textarea_set_cursor_pos(ext->ta, cur);
} else {
lv_textarea_set_cursor_pos(ext->ta, 0);
lv_textarea_add_char(ext->ta, '-');
lv_textarea_set_cursor_pos(ext->ta, cur + 1);
}
} else {
lv_textarea_add_text(ext->ta, txt);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Signal function of the keyboard
* @param kb pointer to a keyboard object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_keyboard_signal(lv_obj_t *kb, lv_signal_t sign, void *param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(kb, sign, param);
if (res != LV_RES_OK) {
return res;
}
if (sign == LV_SIGNAL_GET_TYPE) {
return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
}
if (sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if (sign == LV_SIGNAL_FOCUS) {
lv_keyboard_ext_t *ext = lv_obj_get_ext_attr(kb);
/*Show the cursor of the Text area if cursor management is enabled*/
if (ext->ta && ext->cursor_mng) {
lv_textarea_set_cursor_hidden(ext->ta, false);
}
} else if (sign == LV_SIGNAL_DEFOCUS) {
lv_keyboard_ext_t *ext = lv_obj_get_ext_attr(kb);
/*Show the cursor of the Text area if cursor management is enabled*/
if (ext->ta && ext->cursor_mng) {
lv_textarea_set_cursor_hidden(ext->ta, true);
}
}
return res;
}
/**
* Update the key map for the current mode
* @param kb pointer to a keyboard object
*/
static void lv_keyboard_update_map(lv_obj_t *kb)
{
lv_keyboard_ext_t *ext = lv_obj_get_ext_attr(kb);
lv_btnmatrix_set_map(kb, kb_map[ext->mode]);
lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[ext->mode]);
}
#endif