1
2 /**
3 * @file lv_kb.c
4 *
5 */
6
7 /*********************
8 * INCLUDES
9 *********************/
10 #include "lv_keyboard.h"
11 #if LV_USE_KEYBOARD != 0
12
13 #include "../lv_misc/lv_debug.h"
14 #include "../lv_themes/lv_theme.h"
15 #include "lv_textarea.h"
16
17 /*********************
18 * DEFINES
19 *********************/
20 #define LV_OBJX_NAME "lv_keyboard"
21
22 /**********************
23 * TYPEDEFS
24 **********************/
25
26 /**********************
27 * STATIC PROTOTYPES
28 **********************/
29 static lv_res_t lv_keyboard_signal(lv_obj_t * kb, lv_signal_t sign, void * param);
30 static void lv_keyboard_update_map(lv_obj_t * kb);
31
32 /**********************
33 * STATIC VARIABLES
34 **********************/
35 static lv_signal_cb_t ancestor_signal;
36 /* clang-format off */
37 static const char * const default_kb_map_lc[] = {"1#", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", LV_SYMBOL_BACKSPACE, "\n",
38 "ABC", "a", "s", "d", "f", "g", "h", "j", "k", "l", LV_SYMBOL_NEW_LINE, "\n",
39 "_", "-", "z", "x", "c", "v", "b", "n", "m", ".", ",", ":", "\n",
40 LV_SYMBOL_CLOSE, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
41 };
42
43 static const lv_btnmatrix_ctrl_t default_kb_ctrl_lc_map[] = {
44 LV_KEYBOARD_CTRL_BTN_FLAGS | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7,
45 LV_KEYBOARD_CTRL_BTN_FLAGS | 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7,
46 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
47 LV_KEYBOARD_CTRL_BTN_FLAGS | 2, 2, 6, 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
48 };
49
50 static const char * const default_kb_map_uc[] = {"1#", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", LV_SYMBOL_BACKSPACE, "\n",
51 "abc", "A", "S", "D", "F", "G", "H", "J", "K", "L", LV_SYMBOL_NEW_LINE, "\n",
52 "_", "-", "Z", "X", "C", "V", "B", "N", "M", ".", ",", ":", "\n",
53 LV_SYMBOL_CLOSE, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
54 };
55
56 static const lv_btnmatrix_ctrl_t default_kb_ctrl_uc_map[] = {
57 LV_KEYBOARD_CTRL_BTN_FLAGS | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7,
58 LV_KEYBOARD_CTRL_BTN_FLAGS | 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7,
59 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
60 LV_KEYBOARD_CTRL_BTN_FLAGS | 2, 2, 6, 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
61 };
62
63 static const char * const default_kb_map_spec[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", LV_SYMBOL_BACKSPACE, "\n",
64 "abc", "+", "-", "/", "*", "=", "%", "!", "?", "#", "<", ">", "\n",
65 "\\", "@", "$", "(", ")", "{", "}", "[", "]", ";", "\"", "'", "\n",
66 LV_SYMBOL_CLOSE, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
67 };
68
69 static const lv_btnmatrix_ctrl_t default_kb_ctrl_spec_map[] = {
70 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
71 LV_KEYBOARD_CTRL_BTN_FLAGS | 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
72 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
73 LV_KEYBOARD_CTRL_BTN_FLAGS | 2, 2, 6, 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
74 };
75
76 static const char * const default_kb_map_num[] = {"1", "2", "3", LV_SYMBOL_CLOSE, "\n",
77 "4", "5", "6", LV_SYMBOL_OK, "\n",
78 "7", "8", "9", LV_SYMBOL_BACKSPACE, "\n",
79 "+/-", "0", ".", LV_SYMBOL_LEFT, LV_SYMBOL_RIGHT, ""
80 };
81
82 static const lv_btnmatrix_ctrl_t default_kb_ctrl_num_map[] = {
83 1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
84 1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
85 1, 1, 1, 2,
86 1, 1, 1, 1, 1
87 };
88 /* clang-format on */
89
90 static const char * * kb_map[4] = {
91 (const char * *)default_kb_map_lc,
92 (const char * *)default_kb_map_uc,
93 (const char * *)default_kb_map_spec,
94 (const char * *)default_kb_map_num
95 };
96 static const lv_btnmatrix_ctrl_t * kb_ctrl[4] = {
97 default_kb_ctrl_lc_map,
98 default_kb_ctrl_uc_map,
99 default_kb_ctrl_spec_map,
100 default_kb_ctrl_num_map
101 };
102
103 /**********************
104 * MACROS
105 **********************/
106
107 /**********************
108 * GLOBAL FUNCTIONS
109 **********************/
110
111 /**
112 * Create a keyboard objects
113 * @param par pointer to an object, it will be the parent of the new keyboard
114 * @param copy pointer to a keyboard object, if not NULL then the new object will be copied from it
115 * @return pointer to the created keyboard
116 */
lv_keyboard_create(lv_obj_t * par,const lv_obj_t * copy)117 lv_obj_t * lv_keyboard_create(lv_obj_t * par, const lv_obj_t * copy)
118 {
119 LV_LOG_TRACE("keyboard create started");
120
121 /*Create the ancestor of keyboard*/
122 lv_obj_t * kb = lv_btnmatrix_create(par, copy);
123 LV_ASSERT_MEM(kb);
124 if(kb == NULL) return NULL;
125
126 if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(kb);
127
128 /*Allocate the keyboard type specific extended data*/
129 lv_keyboard_ext_t * ext = lv_obj_allocate_ext_attr(kb, sizeof(lv_keyboard_ext_t));
130 LV_ASSERT_MEM(ext);
131 if(ext == NULL) {
132 lv_obj_del(kb);
133 return NULL;
134 }
135
136 /*Initialize the allocated 'ext' */
137 ext->ta = NULL;
138 ext->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
139 ext->cursor_mng = 0;
140
141 /*The signal and design functions are not copied so set them here*/
142 lv_obj_set_signal_cb(kb, lv_keyboard_signal);
143
144 /*Init the new keyboard keyboard*/
145 if(copy == NULL) {
146 /* Set a size which fits into the parent.
147 * Don't use `par` directly because if the window is created on a page it is moved to the
148 * scrollable so the parent has changed */
149 lv_obj_set_size(kb, lv_obj_get_width_fit(lv_obj_get_parent(kb)),
150 lv_obj_get_height_fit(lv_obj_get_parent(kb)) / 2);
151 lv_obj_align(kb, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
152 lv_obj_set_event_cb(kb, lv_keyboard_def_event_cb);
153 lv_obj_set_base_dir(kb, LV_BIDI_DIR_LTR);
154 lv_obj_add_protect(kb, LV_PROTECT_CLICK_FOCUS);
155
156 lv_btnmatrix_set_map(kb, kb_map[ext->mode]);
157 lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[ext->mode]);
158
159 lv_theme_apply(kb, LV_THEME_KEYBOARD);
160 }
161 /*Copy an existing keyboard*/
162 else {
163 lv_keyboard_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
164 ext->ta = copy_ext->ta;
165 ext->mode = copy_ext->mode;
166 ext->cursor_mng = copy_ext->cursor_mng;
167
168 lv_btnmatrix_set_map(kb, kb_map[ext->mode]);
169 lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[ext->mode]);
170
171 /*Refresh the style with new signal function*/
172 // lv_obj_refresh_style(new_kb);
173 }
174
175 LV_LOG_INFO("keyboard created");
176
177 return kb;
178 }
179
180 /*=====================
181 * Setter functions
182 *====================*/
183
184 /**
185 * Assign a Text Area to the Keyboard. The pressed characters will be put there.
186 * @param kb pointer to a Keyboard object
187 * @param ta pointer to a Text Area object to write there
188 */
lv_keyboard_set_textarea(lv_obj_t * kb,lv_obj_t * ta)189 void lv_keyboard_set_textarea(lv_obj_t * kb, lv_obj_t * ta)
190 {
191 LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
192 if(ta) {
193 LV_ASSERT_OBJ(ta, "lv_textarea");
194 }
195
196 lv_keyboard_ext_t * ext = lv_obj_get_ext_attr(kb);
197
198 /*Hide the cursor of the old Text area if cursor management is enabled*/
199 if(ext->ta && ext->cursor_mng) {
200 lv_textarea_set_cursor_hidden(ext->ta, true);
201 }
202
203 ext->ta = ta;
204
205 /*Show the cursor of the new Text area if cursor management is enabled*/
206 if(ext->ta && ext->cursor_mng) {
207 lv_textarea_set_cursor_hidden(ext->ta, false);
208 }
209 }
210
211 /**
212 * Set a new a mode (text or number map)
213 * @param kb pointer to a Keyboard object
214 * @param mode the mode from 'lv_keyboard_mode_t'
215 */
lv_keyboard_set_mode(lv_obj_t * kb,lv_keyboard_mode_t mode)216 void lv_keyboard_set_mode(lv_obj_t * kb, lv_keyboard_mode_t mode)
217 {
218 LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
219
220 lv_keyboard_ext_t * ext = lv_obj_get_ext_attr(kb);
221 if(ext->mode == mode) return;
222
223 ext->mode = mode;
224 lv_btnmatrix_set_map(kb, kb_map[mode]);
225 lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[mode]);
226 }
227
228 /**
229 * Automatically hide or show the cursor of Text Area
230 * @param kb pointer to a Keyboard object
231 * @param en true: show cursor on the current text area, false: hide cursor
232 */
lv_keyboard_set_cursor_manage(lv_obj_t * kb,bool en)233 void lv_keyboard_set_cursor_manage(lv_obj_t * kb, bool en)
234 {
235 LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
236
237 lv_keyboard_ext_t * ext = lv_obj_get_ext_attr(kb);
238 if(ext->cursor_mng == en) return;
239
240 ext->cursor_mng = en == false ? 0 : 1;
241
242 if(ext->ta) {
243
244 if(ext->cursor_mng) {
245 lv_textarea_set_cursor_hidden(ext->ta, false);
246 }
247 else {
248 lv_textarea_set_cursor_hidden(ext->ta, true);
249 }
250 }
251 }
252
253 /**
254 * Set a new map for the keyboard
255 * @param kb pointer to a Keyboard object
256 * @param mode keyboard map to alter 'lv_keyboard_mode_t'
257 * @param map pointer to a string array to describe the map.
258 * See 'lv_btnmatrix_set_map()' for more info.
259 */
lv_keyboard_set_map(lv_obj_t * kb,lv_keyboard_mode_t mode,const char * map[])260 void lv_keyboard_set_map(lv_obj_t * kb, lv_keyboard_mode_t mode, const char * map[])
261 {
262 kb_map[mode] = map;
263 lv_keyboard_update_map(kb);
264 }
265
266 /**
267 * Set the button control map (hidden, disabled etc.) for the keyboard. The
268 * control map array will be copied and so may be deallocated after this
269 * function returns.
270 * @param kb pointer to a keyboard object
271 * @param mode keyboard ctrl map to alter 'lv_keyboard_mode_t'
272 * @param ctrl_map pointer to an array of `lv_btn_ctrl_t` control bytes.
273 * See: `lv_btnmatrix_set_ctrl_map` for more details.
274 */
lv_keyboard_set_ctrl_map(lv_obj_t * kb,lv_keyboard_mode_t mode,const lv_btnmatrix_ctrl_t ctrl_map[])275 void lv_keyboard_set_ctrl_map(lv_obj_t * kb, lv_keyboard_mode_t mode, const lv_btnmatrix_ctrl_t ctrl_map[])
276 {
277 kb_ctrl[mode] = ctrl_map;
278 lv_keyboard_update_map(kb);
279 }
280
281 /*=====================
282 * Getter functions
283 *====================*/
284
285 /**
286 * Assign a Text Area to the Keyboard. The pressed characters will be put there.
287 * @param kb pointer to a Keyboard object
288 * @return pointer to the assigned Text Area object
289 */
lv_keyboard_get_textarea(const lv_obj_t * kb)290 lv_obj_t * lv_keyboard_get_textarea(const lv_obj_t * kb)
291 {
292 LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
293
294 lv_keyboard_ext_t * ext = lv_obj_get_ext_attr(kb);
295 return ext->ta;
296 }
297
298 /**
299 * Set a new a mode (text or number map)
300 * @param kb pointer to a Keyboard object
301 * @return the current mode from 'lv_keyboard_mode_t'
302 */
lv_keyboard_get_mode(const lv_obj_t * kb)303 lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t * kb)
304 {
305 LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
306
307 lv_keyboard_ext_t * ext = lv_obj_get_ext_attr(kb);
308 return ext->mode;
309 }
310
311 /**
312 * Get the current cursor manage mode.
313 * @param kb pointer to a Keyboard object
314 * @return true: show cursor on the current text area, false: hide cursor
315 */
lv_keyboard_get_cursor_manage(const lv_obj_t * kb)316 bool lv_keyboard_get_cursor_manage(const lv_obj_t * kb)
317 {
318 LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
319
320 lv_keyboard_ext_t * ext = lv_obj_get_ext_attr(kb);
321 return ext->cursor_mng == 0 ? false : true;
322 }
323
324 /*=====================
325 * Other functions
326 *====================*/
327
328 /**
329 * Default keyboard event to add characters to the Text area and change the map.
330 * If a custom `event_cb` is added to the keyboard this function be called from it to handle the
331 * button clicks
332 * @param kb pointer to a keyboard
333 * @param event the triggering event
334 */
lv_keyboard_def_event_cb(lv_obj_t * kb,lv_event_t event)335 void lv_keyboard_def_event_cb(lv_obj_t * kb, lv_event_t event)
336 {
337 LV_ASSERT_OBJ(kb, LV_OBJX_NAME);
338
339 if(event != LV_EVENT_VALUE_CHANGED) return;
340
341 lv_keyboard_ext_t * ext = lv_obj_get_ext_attr(kb);
342 uint16_t btn_id = lv_btnmatrix_get_active_btn(kb);
343 if(btn_id == LV_BTNMATRIX_BTN_NONE) return;
344 if(lv_btnmatrix_get_btn_ctrl(kb, btn_id, LV_BTNMATRIX_CTRL_HIDDEN | LV_BTNMATRIX_CTRL_DISABLED)) return;
345 if(lv_btnmatrix_get_btn_ctrl(kb, btn_id, LV_BTNMATRIX_CTRL_NO_REPEAT) && event == LV_EVENT_LONG_PRESSED_REPEAT) return;
346
347 const char * txt = lv_btnmatrix_get_active_btn_text(kb);
348 if(txt == NULL) return;
349
350 /*Do the corresponding action according to the text of the button*/
351 if(strcmp(txt, "abc") == 0) {
352 ext->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
353 lv_btnmatrix_set_map(kb, kb_map[LV_KEYBOARD_MODE_TEXT_LOWER]);
354 lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[LV_KEYBOARD_MODE_TEXT_LOWER]);
355 return;
356 }
357 else if(strcmp(txt, "ABC") == 0) {
358 ext->mode = LV_KEYBOARD_MODE_TEXT_UPPER;
359 lv_btnmatrix_set_map(kb, kb_map[LV_KEYBOARD_MODE_TEXT_UPPER]);
360 lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[LV_KEYBOARD_MODE_TEXT_UPPER]);
361 return;
362 }
363 else if(strcmp(txt, "1#") == 0) {
364 ext->mode = LV_KEYBOARD_MODE_SPECIAL;
365 lv_btnmatrix_set_map(kb, kb_map[LV_KEYBOARD_MODE_SPECIAL]);
366 lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[LV_KEYBOARD_MODE_SPECIAL]);
367 return;
368 }
369 else if(strcmp(txt, LV_SYMBOL_CLOSE) == 0) {
370 if(kb->event_cb != lv_keyboard_def_event_cb) {
371 lv_res_t res = lv_event_send(kb, LV_EVENT_CANCEL, NULL);
372 if(res != LV_RES_OK) return;
373 }
374 else {
375 lv_keyboard_set_textarea(kb, NULL); /*De-assign the text area to hide it cursor if needed*/
376 lv_obj_del(kb);
377 return;
378 }
379 return;
380 }
381 else if(strcmp(txt, LV_SYMBOL_OK) == 0) {
382 if(kb->event_cb != lv_keyboard_def_event_cb) {
383 lv_res_t res = lv_event_send(kb, LV_EVENT_APPLY, NULL);
384 if(res != LV_RES_OK) return;
385 }
386 else {
387 lv_keyboard_set_textarea(kb, NULL); /*De-assign the text area to hide it cursor if needed*/
388 }
389 return;
390 }
391
392 /*Add the characters to the text area if set*/
393 if(ext->ta == NULL) return;
394
395 if(strcmp(txt, "Enter") == 0 || strcmp(txt, LV_SYMBOL_NEW_LINE) == 0)
396 lv_textarea_add_char(ext->ta, '\n');
397 else if(strcmp(txt, LV_SYMBOL_LEFT) == 0)
398 lv_textarea_cursor_left(ext->ta);
399 else if(strcmp(txt, LV_SYMBOL_RIGHT) == 0)
400 lv_textarea_cursor_right(ext->ta);
401 else if(strcmp(txt, LV_SYMBOL_BACKSPACE) == 0)
402 lv_textarea_del_char(ext->ta);
403 else if(strcmp(txt, "+/-") == 0) {
404 uint16_t cur = lv_textarea_get_cursor_pos(ext->ta);
405 const char * ta_txt = lv_textarea_get_text(ext->ta);
406 if(ta_txt[0] == '-') {
407 lv_textarea_set_cursor_pos(ext->ta, 1);
408 lv_textarea_del_char(ext->ta);
409 lv_textarea_add_char(ext->ta, '+');
410 lv_textarea_set_cursor_pos(ext->ta, cur);
411 }
412 else if(ta_txt[0] == '+') {
413 lv_textarea_set_cursor_pos(ext->ta, 1);
414 lv_textarea_del_char(ext->ta);
415 lv_textarea_add_char(ext->ta, '-');
416 lv_textarea_set_cursor_pos(ext->ta, cur);
417 }
418 else {
419 lv_textarea_set_cursor_pos(ext->ta, 0);
420 lv_textarea_add_char(ext->ta, '-');
421 lv_textarea_set_cursor_pos(ext->ta, cur + 1);
422 }
423 }
424 else {
425 lv_textarea_add_text(ext->ta, txt);
426 }
427 }
428
429 /**********************
430 * STATIC FUNCTIONS
431 **********************/
432
433 /**
434 * Signal function of the keyboard
435 * @param kb pointer to a keyboard object
436 * @param sign a signal type from lv_signal_t enum
437 * @param param pointer to a signal specific variable
438 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
439 */
lv_keyboard_signal(lv_obj_t * kb,lv_signal_t sign,void * param)440 static lv_res_t lv_keyboard_signal(lv_obj_t * kb, lv_signal_t sign, void * param)
441 {
442 lv_res_t res;
443
444 /* Include the ancient signal function */
445 res = ancestor_signal(kb, sign, param);
446 if(res != LV_RES_OK) return res;
447 if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
448
449 if(sign == LV_SIGNAL_CLEANUP) {
450 /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
451 }
452 else if(sign == LV_SIGNAL_FOCUS) {
453 lv_keyboard_ext_t * ext = lv_obj_get_ext_attr(kb);
454 /*Show the cursor of the Text area if cursor management is enabled*/
455 if(ext->ta && ext->cursor_mng) {
456 lv_textarea_set_cursor_hidden(ext->ta, false);
457 }
458 }
459 else if(sign == LV_SIGNAL_DEFOCUS) {
460 lv_keyboard_ext_t * ext = lv_obj_get_ext_attr(kb);
461 /*Show the cursor of the Text area if cursor management is enabled*/
462 if(ext->ta && ext->cursor_mng) {
463 lv_textarea_set_cursor_hidden(ext->ta, true);
464 }
465 }
466
467 return res;
468 }
469
470 /**
471 * Update the key map for the current mode
472 * @param kb pointer to a keyboard object
473 */
lv_keyboard_update_map(lv_obj_t * kb)474 static void lv_keyboard_update_map(lv_obj_t * kb)
475 {
476 lv_keyboard_ext_t * ext = lv_obj_get_ext_attr(kb);
477 lv_btnmatrix_set_map(kb, kb_map[ext->mode]);
478 lv_btnmatrix_set_ctrl_map(kb, kb_ctrl[ext->mode]);
479 }
480
481 #endif
482