1 /**
2  * @file lv_ta.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_textarea.h"
10 #if LV_USE_TEXTAREA != 0
11 
12 #include <string.h>
13 #include "../misc/lv_assert.h"
14 #include "../core/lv_group.h"
15 #include "../core/lv_refr.h"
16 #include "../core/lv_indev.h"
17 #include "../draw/lv_draw.h"
18 #include "../misc/lv_anim.h"
19 #include "../misc/lv_txt.h"
20 #include "../misc/lv_math.h"
21 
22 /*********************
23  *      DEFINES
24  *********************/
25 #define MY_CLASS &lv_textarea_class
26 
27 /*Test configuration*/
28 #ifndef LV_TEXTAREA_DEF_CURSOR_BLINK_TIME
29     #define LV_TEXTAREA_DEF_CURSOR_BLINK_TIME 400 /*ms*/
30 #endif
31 
32 #ifndef LV_TEXTAREA_DEF_PWD_SHOW_TIME
33     #define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
34 #endif
35 
36 #define LV_TEXTAREA_PWD_BULLET_UNICODE      0x2022
37 
38 /**********************
39  *      TYPEDEFS
40  **********************/
41 
42 /**********************
43  *  STATIC PROTOTYPES
44  **********************/
45 static void lv_textarea_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
46 static void lv_textarea_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
47 static void lv_textarea_event(const lv_obj_class_t * class_p, lv_event_t * e);
48 static void label_event_cb(lv_event_t * e);
49 static void cursor_blink_anim_cb(void * obj, int32_t show);
50 static void pwd_char_hider_anim(void * obj, int32_t x);
51 static void pwd_char_hider_anim_ready(lv_anim_t * a);
52 static void pwd_char_hider(lv_obj_t * obj);
53 static bool char_is_accepted(lv_obj_t * obj, uint32_t c);
54 static void start_cursor_blink(lv_obj_t * obj);
55 static void refr_cursor_area(lv_obj_t * obj);
56 static void update_cursor_position_on_click(lv_event_t * e);
57 static lv_res_t insert_handler(lv_obj_t * obj, const char * txt);
58 static void draw_placeholder(lv_event_t * e);
59 static void draw_cursor(lv_event_t * e);
60 
61 /**********************
62  *  STATIC VARIABLES
63  **********************/
64 const lv_obj_class_t lv_textarea_class = {
65     .constructor_cb = lv_textarea_constructor,
66     .destructor_cb = lv_textarea_destructor,
67     .event_cb = lv_textarea_event,
68     .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
69     .width_def = LV_DPI_DEF * 2,
70     .height_def = LV_DPI_DEF,
71     .instance_size = sizeof(lv_textarea_t),
72     .base_class = &lv_obj_class
73 };
74 
75 static const char * ta_insert_replace;
76 
77 /**********************
78  *      MACROS
79  **********************/
80 
81 /**********************
82  *   GLOBAL FUNCTIONS
83  **********************/
84 
lv_textarea_create(lv_obj_t * parent)85 lv_obj_t * lv_textarea_create(lv_obj_t * parent)
86 {
87     LV_LOG_INFO("begin");
88     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
89     lv_obj_class_init_obj(obj);
90     return obj;
91 }
92 
93 /*======================
94  * Add/remove functions
95  *=====================*/
96 
lv_textarea_add_char(lv_obj_t * obj,uint32_t c)97 void lv_textarea_add_char(lv_obj_t * obj, uint32_t c)
98 {
99     LV_ASSERT_OBJ(obj, MY_CLASS);
100 
101     lv_textarea_t * ta = (lv_textarea_t *)obj;
102 
103     const char * letter_buf;
104 
105     uint32_t u32_buf[2];
106     u32_buf[0] = c;
107     u32_buf[1] = 0;
108 
109     letter_buf = (char *)&u32_buf;
110 
111 #if LV_BIG_ENDIAN_SYSTEM
112     if(c != 0) while(*letter_buf == 0) ++letter_buf;
113 #endif
114 
115     lv_res_t res = insert_handler(obj, letter_buf);
116     if(res != LV_RES_OK) return;
117 
118     if(ta->one_line && (c == '\n' || c == '\r')) {
119         LV_LOG_INFO("Text area: line break ignored in one-line mode");
120         return;
121     }
122 
123     uint32_t c_uni = _lv_txt_encoded_next((const char *)&c, NULL);
124 
125     if(char_is_accepted(obj, c_uni) == false) {
126         LV_LOG_INFO("Character is not accepted by the text area (too long text or not in the accepted list)");
127         return;
128     }
129 
130     if(ta->pwd_mode != 0) pwd_char_hider(obj); /*Make sure all the current text contains only '*'*/
131 
132     /*If the textarea is empty, invalidate it to hide the placeholder*/
133     if(ta->placeholder_txt) {
134         const char * txt = lv_label_get_text(ta->label);
135         if(txt[0] == '\0') lv_obj_invalidate(obj);
136     }
137 
138     lv_label_ins_text(ta->label, ta->cursor.pos, letter_buf); /*Insert the character*/
139     lv_textarea_clear_selection(obj);                                                /*Clear selection*/
140 
141     if(ta->pwd_mode != 0) {
142         ta->pwd_tmp = lv_mem_realloc(ta->pwd_tmp, strlen(ta->pwd_tmp) + strlen(letter_buf) + 1); /*+2: the new char + \0*/
143         LV_ASSERT_MALLOC(ta->pwd_tmp);
144         if(ta->pwd_tmp == NULL) return;
145 
146         _lv_txt_ins(ta->pwd_tmp, ta->cursor.pos, (const char *)letter_buf);
147 
148         /*Auto hide characters*/
149         if(ta->pwd_show_time == 0) {
150             pwd_char_hider(obj);
151         }
152         else {
153             lv_anim_t a;
154             lv_anim_init(&a);
155             lv_anim_set_var(&a, ta);
156             lv_anim_set_exec_cb(&a, pwd_char_hider_anim);
157             lv_anim_set_time(&a, ta->pwd_show_time);
158             lv_anim_set_values(&a, 0, 1);
159             lv_anim_set_path_cb(&a, lv_anim_path_step);
160             lv_anim_set_ready_cb(&a, pwd_char_hider_anim_ready);
161             lv_anim_start(&a);
162         }
163     }
164 
165     /*Move the cursor after the new character*/
166     lv_textarea_set_cursor_pos(obj, lv_textarea_get_cursor_pos(obj) + 1);
167 
168     lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
169 }
170 
lv_textarea_add_text(lv_obj_t * obj,const char * txt)171 void lv_textarea_add_text(lv_obj_t * obj, const char * txt)
172 {
173     LV_ASSERT_OBJ(obj, MY_CLASS);
174     LV_ASSERT_NULL(txt);
175 
176     lv_textarea_t * ta = (lv_textarea_t *)obj;
177 
178     if(ta->pwd_mode != 0) pwd_char_hider(obj); /*Make sure all the current text contains only '*'*/
179 
180     /*Add the character one-by-one if not all characters are accepted or there is character limit.*/
181     if(lv_textarea_get_accepted_chars(obj) || lv_textarea_get_max_length(obj)) {
182         uint32_t i = 0;
183         while(txt[i] != '\0') {
184             uint32_t c = _lv_txt_encoded_next(txt, &i);
185             lv_textarea_add_char(obj, _lv_txt_unicode_to_encoded(c));
186         }
187         return;
188     }
189 
190     lv_res_t res = insert_handler(obj, txt);
191     if(res != LV_RES_OK) return;
192 
193     /*If the textarea is empty, invalidate it to hide the placeholder*/
194     if(ta->placeholder_txt) {
195         const char * txt_act = lv_label_get_text(ta->label);
196         if(txt_act[0] == '\0') lv_obj_invalidate(obj);
197     }
198 
199     /*Insert the text*/
200     lv_label_ins_text(ta->label, ta->cursor.pos, txt);
201     lv_textarea_clear_selection(obj);
202 
203     if(ta->pwd_mode != 0) {
204         ta->pwd_tmp = lv_mem_realloc(ta->pwd_tmp, strlen(ta->pwd_tmp) + strlen(txt) + 1);
205         LV_ASSERT_MALLOC(ta->pwd_tmp);
206         if(ta->pwd_tmp == NULL) return;
207 
208         _lv_txt_ins(ta->pwd_tmp, ta->cursor.pos, txt);
209 
210         /*Auto hide characters*/
211         if(ta->pwd_show_time == 0) {
212             pwd_char_hider(obj);
213         }
214         else {
215             lv_anim_t a;
216             lv_anim_init(&a);
217             lv_anim_set_var(&a, ta);
218             lv_anim_set_exec_cb(&a, pwd_char_hider_anim);
219             lv_anim_set_time(&a, ta->pwd_show_time);
220             lv_anim_set_values(&a, 0, 1);
221             lv_anim_set_path_cb(&a, lv_anim_path_step);
222             lv_anim_set_ready_cb(&a, pwd_char_hider_anim_ready);
223             lv_anim_start(&a);
224         }
225     }
226 
227     /*Move the cursor after the new text*/
228     lv_textarea_set_cursor_pos(obj, lv_textarea_get_cursor_pos(obj) + _lv_txt_get_encoded_length(txt));
229 
230     lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
231 }
232 
lv_textarea_del_char(lv_obj_t * obj)233 void lv_textarea_del_char(lv_obj_t * obj)
234 {
235     LV_ASSERT_OBJ(obj, MY_CLASS);
236 
237     lv_textarea_t * ta = (lv_textarea_t *)obj;
238     uint32_t cur_pos  = ta->cursor.pos;
239 
240     if(cur_pos == 0) return;
241 
242     char del_buf[2]   = {LV_KEY_DEL, '\0'};
243 
244     lv_res_t res = insert_handler(obj, del_buf);
245     if(res != LV_RES_OK) return;
246 
247     char * label_txt = lv_label_get_text(ta->label);
248 
249     /*Delete a character*/
250     _lv_txt_cut(label_txt, ta->cursor.pos - 1, 1);
251 
252     /*Refresh the label*/
253     lv_label_set_text(ta->label, label_txt);
254     lv_textarea_clear_selection(obj);
255 
256     /*If the textarea became empty, invalidate it to hide the placeholder*/
257     if(ta->placeholder_txt) {
258         const char * txt = lv_label_get_text(ta->label);
259         if(txt[0] == '\0') lv_obj_invalidate(obj);
260     }
261 
262     if(ta->pwd_mode != 0) {
263         _lv_txt_cut(ta->pwd_tmp, ta->cursor.pos - 1, 1);
264 
265         ta->pwd_tmp = lv_mem_realloc(ta->pwd_tmp, strlen(ta->pwd_tmp) + 1);
266         LV_ASSERT_MALLOC(ta->pwd_tmp);
267         if(ta->pwd_tmp == NULL) return;
268     }
269 
270     /*Move the cursor to the place of the deleted character*/
271     lv_textarea_set_cursor_pos(obj, ta->cursor.pos - 1);
272 
273     lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
274 
275 }
276 
lv_textarea_del_char_forward(lv_obj_t * obj)277 void lv_textarea_del_char_forward(lv_obj_t * obj)
278 {
279     LV_ASSERT_OBJ(obj, MY_CLASS);
280 
281     uint32_t cp = lv_textarea_get_cursor_pos(obj);
282     lv_textarea_set_cursor_pos(obj, cp + 1);
283     if(cp != lv_textarea_get_cursor_pos(obj)) lv_textarea_del_char(obj);
284 }
285 
286 /*=====================
287  * Setter functions
288  *====================*/
289 
lv_textarea_set_text(lv_obj_t * obj,const char * txt)290 void lv_textarea_set_text(lv_obj_t * obj, const char * txt)
291 {
292     LV_ASSERT_OBJ(obj, MY_CLASS);
293     LV_ASSERT_NULL(txt);
294 
295     lv_textarea_t * ta = (lv_textarea_t *)obj;
296 
297     /*Clear the existing selection*/
298     lv_textarea_clear_selection(obj);
299 
300     /*Add the character one-by-one if not all characters are accepted or there is character limit.*/
301     if(lv_textarea_get_accepted_chars(obj) || lv_textarea_get_max_length(obj)) {
302         lv_label_set_text(ta->label, "");
303         lv_textarea_set_cursor_pos(obj, LV_TEXTAREA_CURSOR_LAST);
304         if(ta->pwd_mode != 0) {
305             ta->pwd_tmp[0] = '\0'; /*Clear the password too*/
306         }
307         uint32_t i = 0;
308         while(txt[i] != '\0') {
309             uint32_t c = _lv_txt_encoded_next(txt, &i);
310             lv_textarea_add_char(obj, _lv_txt_unicode_to_encoded(c));
311         }
312     }
313     else {
314         lv_label_set_text(ta->label, txt);
315         lv_textarea_set_cursor_pos(obj, LV_TEXTAREA_CURSOR_LAST);
316     }
317 
318     /*If the textarea is empty, invalidate it to hide the placeholder*/
319     if(ta->placeholder_txt) {
320         const char * txt_act = lv_label_get_text(ta->label);
321         if(txt_act[0] == '\0') lv_obj_invalidate(obj);
322     }
323 
324     if(ta->pwd_mode != 0) {
325         ta->pwd_tmp = lv_mem_realloc(ta->pwd_tmp, strlen(txt) + 1);
326         LV_ASSERT_MALLOC(ta->pwd_tmp);
327         if(ta->pwd_tmp == NULL) return;
328         strcpy(ta->pwd_tmp, txt);
329 
330         /*Auto hide characters*/
331         if(ta->pwd_show_time == 0) {
332             pwd_char_hider(obj);
333         }
334         else {
335             lv_anim_t a;
336             lv_anim_init(&a);
337             lv_anim_set_var(&a, ta);
338             lv_anim_set_exec_cb(&a, pwd_char_hider_anim);
339             lv_anim_set_time(&a, ta->pwd_show_time);
340             lv_anim_set_values(&a, 0, 1);
341             lv_anim_set_path_cb(&a, lv_anim_path_step);
342             lv_anim_set_ready_cb(&a, pwd_char_hider_anim_ready);
343             lv_anim_start(&a);
344         }
345     }
346 
347     lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
348 }
349 
lv_textarea_set_placeholder_text(lv_obj_t * obj,const char * txt)350 void lv_textarea_set_placeholder_text(lv_obj_t * obj, const char * txt)
351 {
352     LV_ASSERT_OBJ(obj, MY_CLASS);
353     LV_ASSERT_NULL(txt);
354 
355     lv_textarea_t * ta = (lv_textarea_t *)obj;
356 
357     size_t txt_len = strlen(txt);
358 
359     if(txt_len == 0) {
360         if(ta->placeholder_txt) {
361             lv_mem_free(ta->placeholder_txt);
362             ta->placeholder_txt = NULL;
363         }
364     }
365     else {
366 
367         /*Allocate memory for the placeholder_txt text*/
368         if(ta->placeholder_txt == NULL) {
369             ta->placeholder_txt = lv_mem_alloc(txt_len + 1);
370         }
371         else {
372             ta->placeholder_txt = lv_mem_realloc(ta->placeholder_txt, txt_len + 1);
373 
374         }
375         LV_ASSERT_MALLOC(ta->placeholder_txt);
376         if(ta->placeholder_txt == NULL) {
377             LV_LOG_ERROR("lv_textarea_set_placeholder_text: couldn't allocate memory for placeholder");
378             return;
379         }
380         strcpy(ta->placeholder_txt, txt);
381     }
382 
383     lv_obj_invalidate(obj);
384 }
385 
lv_textarea_set_cursor_pos(lv_obj_t * obj,int32_t pos)386 void lv_textarea_set_cursor_pos(lv_obj_t * obj, int32_t pos)
387 {
388     LV_ASSERT_OBJ(obj, MY_CLASS);
389 
390     lv_textarea_t * ta = (lv_textarea_t *)obj;
391     if((uint32_t)ta->cursor.pos == (uint32_t)pos) return;
392 
393     uint32_t len = _lv_txt_get_encoded_length(lv_label_get_text(ta->label));
394 
395     if(pos < 0) pos = len + pos;
396 
397     if(pos > (int32_t)len || pos == LV_TEXTAREA_CURSOR_LAST) pos = len;
398 
399     ta->cursor.pos = pos;
400 
401     /*Position the label to make the cursor visible*/
402     lv_obj_update_layout(obj);
403 
404     lv_point_t cur_pos;
405     const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
406     lv_label_get_letter_pos(ta->label, pos, &cur_pos);
407 
408     /*The text area needs to have it's final size to see if the cursor is out of the area or not*/
409 
410     /*Check the top*/
411     lv_coord_t font_h = lv_font_get_line_height(font);
412     if(cur_pos.y < lv_obj_get_scroll_top(obj)) {
413         lv_obj_scroll_to_y(obj, cur_pos.y, LV_ANIM_ON);
414     }
415     /*Check the bottom*/
416     lv_coord_t h = lv_obj_get_content_height(obj);
417     if(cur_pos.y + font_h - lv_obj_get_scroll_top(obj) > h) {
418         lv_obj_scroll_to_y(obj, cur_pos.y - h + font_h, LV_ANIM_ON);
419     }
420 
421     /*Check the left*/
422     if(cur_pos.x < lv_obj_get_scroll_left(obj)) {
423         lv_obj_scroll_to_x(obj, cur_pos.x, LV_ANIM_ON);
424     }
425     /*Check the right*/
426     lv_coord_t w = lv_obj_get_content_width(obj);
427     if(cur_pos.x + font_h - lv_obj_get_scroll_left(obj) > w) {
428         lv_obj_scroll_to_x(obj, cur_pos.x - w + font_h, LV_ANIM_ON);
429     }
430 
431     ta->cursor.valid_x = cur_pos.x;
432 
433     start_cursor_blink(obj);
434 
435     refr_cursor_area(obj);
436 }
437 
lv_textarea_set_cursor_click_pos(lv_obj_t * obj,bool en)438 void lv_textarea_set_cursor_click_pos(lv_obj_t * obj, bool en)
439 {
440     LV_ASSERT_OBJ(obj, MY_CLASS);
441 
442     lv_textarea_t * ta = (lv_textarea_t *)obj;
443     ta->cursor.click_pos = en ? 1 : 0;
444 }
445 
lv_textarea_set_password_mode(lv_obj_t * obj,bool en)446 void lv_textarea_set_password_mode(lv_obj_t * obj, bool en)
447 {
448     LV_ASSERT_OBJ(obj, MY_CLASS);
449 
450     lv_textarea_t * ta = (lv_textarea_t *)obj;
451     if(ta->pwd_mode == en) return;
452 
453     ta->pwd_mode = en == false ? 0 : 1;
454     /*Pwd mode is now enabled*/
455     if(en != false) {
456         char * txt   = lv_label_get_text(ta->label);
457         size_t len = strlen(txt);
458         ta->pwd_tmp = lv_mem_alloc(len + 1);
459         LV_ASSERT_MALLOC(ta->pwd_tmp);
460         if(ta->pwd_tmp == NULL) return;
461 
462         strcpy(ta->pwd_tmp, txt);
463 
464         pwd_char_hider(obj);
465 
466         lv_textarea_clear_selection(obj);
467     }
468     /*Pwd mode is now disabled*/
469     else {
470         lv_textarea_clear_selection(obj);
471         lv_label_set_text(ta->label, ta->pwd_tmp);
472         lv_mem_free(ta->pwd_tmp);
473         ta->pwd_tmp = NULL;
474     }
475 
476     refr_cursor_area(obj);
477 }
478 
lv_textarea_set_one_line(lv_obj_t * obj,bool en)479 void lv_textarea_set_one_line(lv_obj_t * obj, bool en)
480 {
481     LV_ASSERT_OBJ(obj, MY_CLASS);
482 
483     lv_textarea_t * ta = (lv_textarea_t *)obj;
484     if(ta->one_line == en) return;
485 
486     if(en) {
487         ta->one_line = 1;
488         lv_obj_set_width(ta->label, LV_SIZE_CONTENT);
489         lv_obj_set_style_min_width(ta->label, lv_pct(100), 0);
490 
491         lv_obj_set_height(obj, LV_SIZE_CONTENT);
492         lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF);
493     }
494     else {
495         ta->one_line = 0;
496         lv_obj_set_width(ta->label, lv_pct(100));
497         lv_obj_set_style_min_width(ta->label, 0, 0);
498         lv_obj_remove_local_style_prop(obj, LV_STYLE_HEIGHT, LV_PART_MAIN);
499         lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF);
500     }
501 }
502 
lv_textarea_set_accepted_chars(lv_obj_t * obj,const char * list)503 void lv_textarea_set_accepted_chars(lv_obj_t * obj, const char * list)
504 {
505     LV_ASSERT_OBJ(obj, MY_CLASS);
506 
507     lv_textarea_t * ta = (lv_textarea_t *)obj;
508 
509     ta->accepted_chars = list;
510 }
511 
lv_textarea_set_max_length(lv_obj_t * obj,uint32_t num)512 void lv_textarea_set_max_length(lv_obj_t * obj, uint32_t num)
513 {
514     LV_ASSERT_OBJ(obj, MY_CLASS);
515 
516     lv_textarea_t * ta = (lv_textarea_t *)obj;
517 
518     ta->max_length = num;
519 }
520 
lv_textarea_set_insert_replace(lv_obj_t * obj,const char * txt)521 void lv_textarea_set_insert_replace(lv_obj_t * obj, const char * txt)
522 {
523     LV_ASSERT_OBJ(obj, MY_CLASS);
524 
525     LV_UNUSED(obj);
526     ta_insert_replace = txt;
527 }
528 
lv_textarea_set_text_selection(lv_obj_t * obj,bool en)529 void lv_textarea_set_text_selection(lv_obj_t * obj, bool en)
530 {
531     LV_ASSERT_OBJ(obj, MY_CLASS);
532 
533 #if LV_LABEL_TEXT_SELECTION
534     lv_textarea_t * ta = (lv_textarea_t *)obj;
535 
536     ta->text_sel_en = en;
537 
538     if(!en) lv_textarea_clear_selection(obj);
539 #else
540     LV_UNUSED(obj); /*Unused*/
541     LV_UNUSED(en);  /*Unused*/
542 #endif
543 }
544 
lv_textarea_set_password_show_time(lv_obj_t * obj,uint16_t time)545 void lv_textarea_set_password_show_time(lv_obj_t * obj, uint16_t time)
546 {
547     LV_ASSERT_OBJ(obj, MY_CLASS);
548 
549     lv_textarea_t * ta = (lv_textarea_t *)obj;
550     ta->pwd_show_time = time;
551 }
552 
lv_textarea_set_align(lv_obj_t * obj,lv_text_align_t align)553 void lv_textarea_set_align(lv_obj_t * obj, lv_text_align_t align)
554 {
555     LV_LOG_WARN("Deprecated: use the normal text_align style property instead");
556     lv_obj_set_style_text_align(obj, align, 0);
557 
558     switch(align) {
559         default:
560         case LV_TEXT_ALIGN_LEFT:
561             lv_obj_align(lv_textarea_get_label(obj), LV_ALIGN_TOP_LEFT, 0, 0);
562             break;
563         case LV_TEXT_ALIGN_RIGHT:
564             lv_obj_align(lv_textarea_get_label(obj), LV_ALIGN_TOP_RIGHT, 0, 0);
565             break;
566         case LV_TEXT_ALIGN_CENTER:
567             lv_obj_align(lv_textarea_get_label(obj), LV_ALIGN_TOP_MID, 0, 0);
568             break;
569     }
570 }
571 
572 /*=====================
573  * Getter functions
574  *====================*/
575 
lv_textarea_get_text(const lv_obj_t * obj)576 const char * lv_textarea_get_text(const lv_obj_t * obj)
577 {
578     LV_ASSERT_OBJ(obj, MY_CLASS);
579 
580     lv_textarea_t * ta = (lv_textarea_t *)obj;
581 
582     const char * txt;
583     if(ta->pwd_mode == 0) {
584         txt = lv_label_get_text(ta->label);
585     }
586     else {
587         txt = ta->pwd_tmp;
588     }
589 
590     return txt;
591 }
592 
lv_textarea_get_placeholder_text(lv_obj_t * obj)593 const char * lv_textarea_get_placeholder_text(lv_obj_t * obj)
594 {
595     LV_ASSERT_OBJ(obj, MY_CLASS);
596 
597     lv_textarea_t * ta = (lv_textarea_t *)obj;
598     if(ta->placeholder_txt) return ta->placeholder_txt;
599     else return "";
600 }
601 
lv_textarea_get_label(const lv_obj_t * obj)602 lv_obj_t * lv_textarea_get_label(const lv_obj_t * obj)
603 {
604     LV_ASSERT_OBJ(obj, MY_CLASS);
605 
606     lv_textarea_t * ta = (lv_textarea_t *)obj;
607     return ta->label;
608 }
609 
lv_textarea_get_cursor_pos(const lv_obj_t * obj)610 uint32_t lv_textarea_get_cursor_pos(const lv_obj_t * obj)
611 {
612     LV_ASSERT_OBJ(obj, MY_CLASS);
613 
614     lv_textarea_t * ta = (lv_textarea_t *)obj;
615     return ta->cursor.pos;
616 }
617 
lv_textarea_get_cursor_click_pos(lv_obj_t * obj)618 bool lv_textarea_get_cursor_click_pos(lv_obj_t * obj)
619 {
620     LV_ASSERT_OBJ(obj, MY_CLASS);
621 
622     lv_textarea_t * ta = (lv_textarea_t *)obj;
623     return ta->cursor.click_pos ? true : false;
624 }
625 
lv_textarea_get_password_mode(const lv_obj_t * obj)626 bool lv_textarea_get_password_mode(const lv_obj_t * obj)
627 {
628     LV_ASSERT_OBJ(obj, MY_CLASS);
629 
630     lv_textarea_t * ta = (lv_textarea_t *)obj;
631     return ta->pwd_mode == 0 ? false : true;
632 }
633 
lv_textarea_get_one_line(const lv_obj_t * obj)634 bool lv_textarea_get_one_line(const lv_obj_t * obj)
635 {
636     LV_ASSERT_OBJ(obj, MY_CLASS);
637 
638     lv_textarea_t * ta = (lv_textarea_t *)obj;
639     return ta->one_line == 0 ? false : true;
640 }
641 
lv_textarea_get_accepted_chars(lv_obj_t * obj)642 const char * lv_textarea_get_accepted_chars(lv_obj_t * obj)
643 {
644     LV_ASSERT_OBJ(obj, MY_CLASS);
645 
646     lv_textarea_t * ta = (lv_textarea_t *)obj;
647 
648     return ta->accepted_chars;
649 }
650 
lv_textarea_get_max_length(lv_obj_t * obj)651 uint32_t lv_textarea_get_max_length(lv_obj_t * obj)
652 {
653     LV_ASSERT_OBJ(obj, MY_CLASS);
654 
655     lv_textarea_t * ta = (lv_textarea_t *)obj;
656     return ta->max_length;
657 }
658 
lv_textarea_text_is_selected(const lv_obj_t * obj)659 bool lv_textarea_text_is_selected(const lv_obj_t * obj)
660 {
661     LV_ASSERT_OBJ(obj, MY_CLASS);
662 
663 #if LV_LABEL_TEXT_SELECTION
664     lv_textarea_t * ta = (lv_textarea_t *)obj;
665 
666     if((lv_label_get_text_selection_start(ta->label) != LV_DRAW_LABEL_NO_TXT_SEL ||
667         lv_label_get_text_selection_end(ta->label) != LV_DRAW_LABEL_NO_TXT_SEL)) {
668         return true;
669     }
670     else {
671         return false;
672     }
673 #else
674     LV_UNUSED(obj); /*Unused*/
675     return false;
676 #endif
677 }
678 
lv_textarea_get_text_selection(lv_obj_t * obj)679 bool lv_textarea_get_text_selection(lv_obj_t * obj)
680 {
681     LV_ASSERT_OBJ(obj, MY_CLASS);
682 
683 #if LV_LABEL_TEXT_SELECTION
684     lv_textarea_t * ta = (lv_textarea_t *)obj;
685     return ta->text_sel_en;
686 #else
687     LV_UNUSED(obj); /*Unused*/
688     return false;
689 #endif
690 }
691 
lv_textarea_get_password_show_time(lv_obj_t * obj)692 uint16_t lv_textarea_get_password_show_time(lv_obj_t * obj)
693 {
694     LV_ASSERT_OBJ(obj, MY_CLASS);
695 
696     lv_textarea_t * ta = (lv_textarea_t *)obj;
697 
698     return ta->pwd_show_time;
699 }
700 
701 /*=====================
702  * Other functions
703  *====================*/
704 
lv_textarea_clear_selection(lv_obj_t * obj)705 void lv_textarea_clear_selection(lv_obj_t * obj)
706 {
707     LV_ASSERT_OBJ(obj, MY_CLASS);
708 
709 #if LV_LABEL_TEXT_SELECTION
710     lv_textarea_t * ta = (lv_textarea_t *)obj;
711 
712     if(lv_label_get_text_selection_start(ta->label) != LV_DRAW_LABEL_NO_TXT_SEL ||
713        lv_label_get_text_selection_end(ta->label) != LV_DRAW_LABEL_NO_TXT_SEL) {
714         lv_label_set_text_sel_start(ta->label, LV_DRAW_LABEL_NO_TXT_SEL);
715         lv_label_set_text_sel_end(ta->label, LV_DRAW_LABEL_NO_TXT_SEL);
716     }
717 #else
718     LV_UNUSED(obj); /*Unused*/
719 #endif
720 }
721 
lv_textarea_cursor_right(lv_obj_t * obj)722 void lv_textarea_cursor_right(lv_obj_t * obj)
723 {
724     LV_ASSERT_OBJ(obj, MY_CLASS);
725 
726     uint32_t cp = lv_textarea_get_cursor_pos(obj);
727     cp++;
728     lv_textarea_set_cursor_pos(obj, cp);
729 }
730 
lv_textarea_cursor_left(lv_obj_t * obj)731 void lv_textarea_cursor_left(lv_obj_t * obj)
732 {
733     LV_ASSERT_OBJ(obj, MY_CLASS);
734 
735     uint32_t cp = lv_textarea_get_cursor_pos(obj);
736     if(cp > 0) {
737         cp--;
738         lv_textarea_set_cursor_pos(obj, cp);
739     }
740 }
741 
lv_textarea_cursor_down(lv_obj_t * obj)742 void lv_textarea_cursor_down(lv_obj_t * obj)
743 {
744     LV_ASSERT_OBJ(obj, MY_CLASS);
745 
746     lv_textarea_t * ta = (lv_textarea_t *)obj;
747     lv_point_t pos;
748 
749     /*Get the position of the current letter*/
750     lv_label_get_letter_pos(ta->label, lv_textarea_get_cursor_pos(obj), &pos);
751 
752     /*Increment the y with one line and keep the valid x*/
753 
754     lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
755     const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
756     lv_coord_t font_h              = lv_font_get_line_height(font);
757     pos.y += font_h + line_space + 1;
758     pos.x = ta->cursor.valid_x;
759 
760     /*Do not go below the last line*/
761     if(pos.y < lv_obj_get_height(ta->label)) {
762         /*Get the letter index on the new cursor position and set it*/
763         uint32_t new_cur_pos = lv_label_get_letter_on(ta->label, &pos);
764 
765         lv_coord_t cur_valid_x_tmp = ta->cursor.valid_x; /*Cursor position set overwrites the valid position*/
766         lv_textarea_set_cursor_pos(obj, new_cur_pos);
767         ta->cursor.valid_x = cur_valid_x_tmp;
768     }
769 }
770 
lv_textarea_cursor_up(lv_obj_t * obj)771 void lv_textarea_cursor_up(lv_obj_t * obj)
772 {
773     LV_ASSERT_OBJ(obj, MY_CLASS);
774 
775     lv_textarea_t * ta = (lv_textarea_t *)obj;
776     lv_point_t pos;
777 
778     /*Get the position of the current letter*/
779     lv_label_get_letter_pos(ta->label, lv_textarea_get_cursor_pos(obj), &pos);
780 
781     /*Decrement the y with one line and keep the valid x*/
782     lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
783     const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
784     lv_coord_t font_h              = lv_font_get_line_height(font);
785     pos.y -= font_h + line_space - 1;
786     pos.x = ta->cursor.valid_x;
787 
788     /*Get the letter index on the new cursor position and set it*/
789     uint32_t new_cur_pos       = lv_label_get_letter_on(ta->label, &pos);
790     lv_coord_t cur_valid_x_tmp = ta->cursor.valid_x; /*Cursor position set overwrites the valid position*/
791     lv_textarea_set_cursor_pos(obj, new_cur_pos);
792     ta->cursor.valid_x = cur_valid_x_tmp;
793 }
794 
795 /**********************
796  *   STATIC FUNCTIONS
797  **********************/
798 
lv_textarea_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)799 static void lv_textarea_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
800 {
801     LV_UNUSED(class_p);
802     LV_TRACE_OBJ_CREATE("begin");
803 
804     lv_textarea_t * ta = (lv_textarea_t *)obj;
805 
806     ta->pwd_mode          = 0;
807     ta->pwd_tmp           = NULL;
808     ta->pwd_show_time     = LV_TEXTAREA_DEF_PWD_SHOW_TIME;
809     ta->accepted_chars    = NULL;
810     ta->max_length        = 0;
811     ta->cursor.show      = 1;
812     /*It will be set to zero later (with zero value lv_textarea_set_cursor_pos(obj, 0); wouldn't do anything as there is no difference)*/
813     ta->cursor.pos        = 1;
814     ta->cursor.click_pos  = 1;
815     ta->cursor.valid_x    = 0;
816     ta->one_line          = 0;
817 #if LV_LABEL_TEXT_SELECTION
818     ta->text_sel_en = 0;
819 #endif
820     ta->label       = NULL;
821     ta->placeholder_txt = NULL;
822 
823     ta->label = lv_label_create(obj);
824     lv_obj_set_width(ta->label, lv_pct(100));
825     lv_label_set_text(ta->label, "");
826     lv_obj_add_event_cb(ta->label, label_event_cb, LV_EVENT_ALL, NULL);
827     lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
828     lv_textarea_set_cursor_pos(obj, 0);
829 
830     start_cursor_blink(obj);
831 
832     LV_TRACE_OBJ_CREATE("finished");
833 }
834 
lv_textarea_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)835 static void lv_textarea_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
836 {
837     LV_UNUSED(class_p);
838 
839     lv_textarea_t * ta = (lv_textarea_t *)obj;
840     if(ta->pwd_tmp != NULL) {
841         lv_mem_free(ta->pwd_tmp);
842         ta->pwd_tmp = NULL;
843     }
844     if(ta->placeholder_txt != NULL) {
845         lv_mem_free(ta->placeholder_txt);
846         ta->placeholder_txt = NULL;
847     }
848 }
849 
lv_textarea_event(const lv_obj_class_t * class_p,lv_event_t * e)850 static void lv_textarea_event(const lv_obj_class_t * class_p, lv_event_t * e)
851 {
852     LV_UNUSED(class_p);
853 
854     lv_res_t res;
855     /*Call the ancestor's event handler*/
856     res = lv_obj_event_base(MY_CLASS, e);
857     if(res != LV_RES_OK) return;
858 
859     lv_event_code_t code = lv_event_get_code(e);
860     lv_obj_t * obj = lv_event_get_target(e);
861 
862     if(code == LV_EVENT_FOCUSED) {
863         start_cursor_blink(obj);
864     }
865     else if(code == LV_EVENT_KEY) {
866         uint32_t c = *((uint32_t *)lv_event_get_param(e)); /*uint32_t because can be UTF-8*/
867         if(c == LV_KEY_RIGHT)
868             lv_textarea_cursor_right(obj);
869         else if(c == LV_KEY_LEFT)
870             lv_textarea_cursor_left(obj);
871         else if(c == LV_KEY_UP)
872             lv_textarea_cursor_up(obj);
873         else if(c == LV_KEY_DOWN)
874             lv_textarea_cursor_down(obj);
875         else if(c == LV_KEY_BACKSPACE)
876             lv_textarea_del_char(obj);
877         else if(c == LV_KEY_DEL)
878             lv_textarea_del_char_forward(obj);
879         else if(c == LV_KEY_HOME)
880             lv_textarea_set_cursor_pos(obj, 0);
881         else if(c == LV_KEY_END)
882             lv_textarea_set_cursor_pos(obj, LV_TEXTAREA_CURSOR_LAST);
883         else if(c == LV_KEY_ENTER && lv_textarea_get_one_line(obj))
884             lv_event_send(obj, LV_EVENT_READY, NULL);
885         else {
886             lv_textarea_add_char(obj, c);
887         }
888     }
889     else if(code == LV_EVENT_PRESSED || code == LV_EVENT_PRESSING || code == LV_EVENT_PRESS_LOST ||
890             code == LV_EVENT_RELEASED) {
891         update_cursor_position_on_click(e);
892     }
893     else if(code == LV_EVENT_DRAW_MAIN) {
894         draw_placeholder(e);
895     }
896     else if(code == LV_EVENT_DRAW_POST) {
897         draw_cursor(e);
898     }
899 }
900 
label_event_cb(lv_event_t * e)901 static void label_event_cb(lv_event_t * e)
902 {
903     lv_event_code_t code = lv_event_get_code(e);
904     lv_obj_t * label = lv_event_get_target(e);
905     lv_obj_t * ta = lv_obj_get_parent(label);
906 
907     if(code == LV_EVENT_STYLE_CHANGED || code == LV_EVENT_SIZE_CHANGED) {
908         lv_label_set_text(label, NULL);
909         refr_cursor_area(ta);
910         start_cursor_blink(ta);
911     }
912 }
913 
914 
915 
916 /**
917  * Called to blink the cursor
918  * @param ta pointer to a text area
919  * @param hide 1: hide the cursor, 0: show it
920  */
cursor_blink_anim_cb(void * obj,int32_t show)921 static void cursor_blink_anim_cb(void * obj, int32_t show)
922 {
923     lv_textarea_t * ta = (lv_textarea_t *)obj;
924     if(show != ta->cursor.show) {
925         ta->cursor.show = show == 0 ? 0 : 1;
926         lv_area_t area_tmp;
927         lv_area_copy(&area_tmp, &ta->cursor.area);
928         area_tmp.x1 += ta->label->coords.x1;
929         area_tmp.y1 += ta->label->coords.y1;
930         area_tmp.x2 += ta->label->coords.x1;
931         area_tmp.y2 += ta->label->coords.y1;
932         lv_obj_invalidate_area(obj, &area_tmp);
933     }
934 }
935 
936 /**
937  * Dummy function to animate char hiding in pwd mode.
938  * Does nothing, but a function is required in car hiding anim.
939  * (pwd_char_hider callback do the real job)
940  * @param ta unused
941  * @param x unused
942  */
pwd_char_hider_anim(void * obj,int32_t x)943 static void pwd_char_hider_anim(void * obj, int32_t x)
944 {
945     LV_UNUSED(obj);
946     LV_UNUSED(x);
947 }
948 
949 /**
950  * Call when an animation is ready to convert all characters to '*'
951  * @param a pointer to the animation
952  */
pwd_char_hider_anim_ready(lv_anim_t * a)953 static void pwd_char_hider_anim_ready(lv_anim_t * a)
954 {
955     lv_obj_t * obj = a->var;
956     pwd_char_hider(obj);
957 }
958 
959 /**
960  * Hide all characters (convert them to '*')
961  * @param ta pointer to text area object
962  */
pwd_char_hider(lv_obj_t * obj)963 static void pwd_char_hider(lv_obj_t * obj)
964 {
965     lv_textarea_t * ta = (lv_textarea_t *)obj;
966     if(ta->pwd_mode != 0) {
967         char * txt  = lv_label_get_text(ta->label);
968         int32_t enc_len = _lv_txt_get_encoded_length(txt);
969         if(enc_len == 0) return;
970 
971         /*If the textarea's font has "bullet" character use it else fallback to "*"*/
972         const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
973         lv_font_glyph_dsc_t g;
974         bool has_bullet;
975         has_bullet = lv_font_get_glyph_dsc(font, &g, LV_TEXTAREA_PWD_BULLET_UNICODE, 0);
976         const char * bullet;
977         if(has_bullet) bullet = LV_SYMBOL_BULLET;
978         else bullet = "*";
979 
980         size_t bullet_len = strlen(bullet);
981         char * txt_tmp = lv_mem_buf_get(enc_len * bullet_len + 1);
982         int32_t i;
983         for(i = 0; i < enc_len; i++) {
984             lv_memcpy(&txt_tmp[i * bullet_len], bullet, bullet_len);
985         }
986 
987         txt_tmp[i * bullet_len] = '\0';
988 
989         lv_label_set_text(ta->label, txt_tmp);
990         lv_mem_buf_release(txt_tmp);
991         refr_cursor_area(obj);
992     }
993 }
994 
995 /**
996  * Test an unicode character if it is accepted or not. Checks max length and accepted char list.
997  * @param ta pointer to a test area object
998  * @param c an unicode character
999  * @return true: accepted; false: rejected
1000  */
char_is_accepted(lv_obj_t * obj,uint32_t c)1001 static bool char_is_accepted(lv_obj_t * obj, uint32_t c)
1002 {
1003     lv_textarea_t * ta = (lv_textarea_t *)obj;
1004 
1005     /*If no restriction accept it*/
1006     if((ta->accepted_chars == NULL || ta->accepted_chars[0] == '\0') && ta->max_length == 0) return true;
1007 
1008     /*Too many characters?*/
1009     if(ta->max_length > 0 && _lv_txt_get_encoded_length(lv_textarea_get_text(obj)) >= ta->max_length) {
1010         return false;
1011     }
1012 
1013     /*Accepted character?*/
1014     if(ta->accepted_chars) {
1015         uint32_t i = 0;
1016 
1017         while(ta->accepted_chars[i] != '\0') {
1018             uint32_t a = _lv_txt_encoded_next(ta->accepted_chars, &i);
1019             if(a == c) return true; /*Accepted*/
1020         }
1021 
1022         return false; /*The character wasn't in the list*/
1023     }
1024     else {
1025         return true; /*If the accepted char list in not specified the accept the character*/
1026     }
1027 }
1028 
start_cursor_blink(lv_obj_t * obj)1029 static void start_cursor_blink(lv_obj_t * obj)
1030 {
1031     lv_textarea_t * ta = (lv_textarea_t *)obj;
1032     uint32_t blink_time = lv_obj_get_style_anim_time(obj, LV_PART_CURSOR);
1033     if(blink_time == 0) {
1034         lv_anim_del(obj, cursor_blink_anim_cb);
1035         ta->cursor.show = 1;
1036     }
1037     else {
1038         lv_anim_t a;
1039         lv_anim_init(&a);
1040         lv_anim_set_var(&a, ta);
1041         lv_anim_set_exec_cb(&a, cursor_blink_anim_cb);
1042         lv_anim_set_time(&a, blink_time);
1043         lv_anim_set_playback_time(&a, blink_time);
1044         lv_anim_set_values(&a, 1, 0);
1045         lv_anim_set_path_cb(&a, lv_anim_path_step);
1046         lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
1047         lv_anim_start(&a);
1048     }
1049 }
1050 
refr_cursor_area(lv_obj_t * obj)1051 static void refr_cursor_area(lv_obj_t * obj)
1052 {
1053     lv_textarea_t * ta = (lv_textarea_t *)obj;
1054 
1055     const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
1056     lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
1057 
1058     uint32_t cur_pos = lv_textarea_get_cursor_pos(obj);
1059     const char * txt = lv_label_get_text(ta->label);
1060 
1061     uint32_t byte_pos;
1062     byte_pos = _lv_txt_encoded_get_byte_id(txt, cur_pos);
1063 
1064     uint32_t letter = _lv_txt_encoded_next(&txt[byte_pos], NULL);
1065 
1066     lv_coord_t letter_h = lv_font_get_line_height(font);
1067 
1068     /*Set letter_w (set not 0 on non printable but valid chars)*/
1069     lv_coord_t letter_w;
1070     if(letter == '\0' || letter == '\n' || letter == '\r') {
1071         letter_w = lv_font_get_glyph_width(font, ' ', '\0');
1072     }
1073     else {
1074         /*`letter_next` parameter is '\0' to ignore kerning*/
1075         letter_w = lv_font_get_glyph_width(font, letter, '\0');
1076     }
1077 
1078     lv_point_t letter_pos;
1079     lv_label_get_letter_pos(ta->label, cur_pos, &letter_pos);
1080 
1081     lv_text_align_t align = lv_obj_calculate_style_text_align(ta->label, LV_PART_MAIN, lv_label_get_text(ta->label));
1082 
1083     /*If the cursor is out of the text (most right) draw it to the next line*/
1084     if(letter_pos.x + ta->label->coords.x1 + letter_w > ta->label->coords.x2 && ta->one_line == 0 &&
1085        align != LV_TEXT_ALIGN_RIGHT) {
1086         letter_pos.x = 0;
1087         letter_pos.y += letter_h + line_space;
1088 
1089         if(letter != '\0') {
1090             byte_pos += _lv_txt_encoded_size(&txt[byte_pos]);
1091             letter = _lv_txt_encoded_next(&txt[byte_pos], NULL);
1092         }
1093 
1094         if(letter == '\0' || letter == '\n' || letter == '\r') {
1095             letter_w = lv_font_get_glyph_width(font, ' ', '\0');
1096         }
1097         else {
1098             letter_w = lv_font_get_glyph_width(font, letter, '\0');
1099         }
1100     }
1101 
1102     /*Save the byte position. It is required to draw `LV_CURSOR_BLOCK`*/
1103     ta->cursor.txt_byte_pos = byte_pos;
1104 
1105     /*Calculate the cursor according to its type*/
1106     lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_CURSOR);
1107     lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_CURSOR) + border_width;
1108     lv_coord_t bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_CURSOR) + border_width;
1109     lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_CURSOR) + border_width;
1110     lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_CURSOR) + border_width;
1111 
1112     lv_area_t cur_area;
1113     cur_area.x1 = letter_pos.x - left;
1114     cur_area.y1 = letter_pos.y - top;
1115     cur_area.x2 = letter_pos.x + right + letter_w - 1;
1116     cur_area.y2 = letter_pos.y + bottom + letter_h - 1;
1117 
1118     /*Save the new area*/
1119     lv_area_t area_tmp;
1120     lv_area_copy(&area_tmp, &ta->cursor.area);
1121     area_tmp.x1 += ta->label->coords.x1;
1122     area_tmp.y1 += ta->label->coords.y1;
1123     area_tmp.x2 += ta->label->coords.x1;
1124     area_tmp.y2 += ta->label->coords.y1;
1125     lv_obj_invalidate_area(obj, &area_tmp);
1126 
1127     lv_area_copy(&ta->cursor.area, &cur_area);
1128 
1129     lv_area_copy(&area_tmp, &ta->cursor.area);
1130     area_tmp.x1 += ta->label->coords.x1;
1131     area_tmp.y1 += ta->label->coords.y1;
1132     area_tmp.x2 += ta->label->coords.x1;
1133     area_tmp.y2 += ta->label->coords.y1;
1134     lv_obj_invalidate_area(obj, &area_tmp);
1135 }
1136 
update_cursor_position_on_click(lv_event_t * e)1137 static void update_cursor_position_on_click(lv_event_t * e)
1138 {
1139     lv_indev_t * click_source = lv_indev_get_act();
1140     if(click_source == NULL) return;
1141 
1142     lv_event_code_t code = lv_event_get_code(e);
1143     lv_obj_t * obj = lv_event_get_target(e);
1144     lv_textarea_t * ta = (lv_textarea_t *)obj;
1145     if(ta->cursor.click_pos == 0) return;
1146 
1147     if(lv_indev_get_type(click_source) == LV_INDEV_TYPE_KEYPAD ||
1148        lv_indev_get_type(click_source) == LV_INDEV_TYPE_ENCODER) {
1149         return;
1150     }
1151 
1152     lv_area_t label_coords;
1153     lv_obj_get_coords(ta->label, &label_coords);
1154 
1155     lv_point_t point_act, vect_act;
1156     lv_indev_get_point(click_source, &point_act);
1157     lv_indev_get_vect(click_source, &vect_act);
1158 
1159     if(point_act.x < 0 || point_act.y < 0) return; /*Ignore event from keypad*/
1160     lv_point_t rel_pos;
1161     rel_pos.x = point_act.x - label_coords.x1;
1162     rel_pos.y = point_act.y - label_coords.y1;
1163 
1164     lv_coord_t label_width = lv_obj_get_width(ta->label);
1165 
1166     uint16_t char_id_at_click;
1167 
1168 #if LV_LABEL_TEXT_SELECTION
1169     lv_label_t * label_data = (lv_label_t *)ta->label;
1170     bool click_outside_label;
1171     /*Check if the click happened on the left side of the area outside the label*/
1172     if(rel_pos.x < 0) {
1173         char_id_at_click = 0;
1174         click_outside_label       = true;
1175     }
1176     /*Check if the click happened on the right side of the area outside the label*/
1177     else if(rel_pos.x >= label_width) {
1178         char_id_at_click = LV_TEXTAREA_CURSOR_LAST;
1179         click_outside_label       = true;
1180     }
1181     else {
1182         char_id_at_click = lv_label_get_letter_on(ta->label, &rel_pos);
1183         click_outside_label       = !lv_label_is_char_under_pos(ta->label, &rel_pos);
1184     }
1185 
1186     if(ta->text_sel_en) {
1187         if(!ta->text_sel_in_prog && !click_outside_label && code == LV_EVENT_PRESSED) {
1188             /*Input device just went down. Store the selection start position*/
1189             ta->sel_start    = char_id_at_click;
1190             ta->sel_end      = LV_LABEL_TEXT_SELECTION_OFF;
1191             ta->text_sel_in_prog = 1;
1192             lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN);
1193         }
1194         else if(ta->text_sel_in_prog && code == LV_EVENT_PRESSING) {
1195             /*Input device may be moving. Store the end position*/
1196             ta->sel_end = char_id_at_click;
1197         }
1198         else if(ta->text_sel_in_prog && (code == LV_EVENT_PRESS_LOST || code == LV_EVENT_RELEASED)) {
1199             /*Input device is released. Check if anything was selected.*/
1200             lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN);
1201         }
1202     }
1203 
1204     if(ta->text_sel_in_prog || code == LV_EVENT_PRESSED) lv_textarea_set_cursor_pos(obj, char_id_at_click);
1205 
1206     if(ta->text_sel_in_prog) {
1207         /*If the selected area has changed then update the real values and*/
1208 
1209         /*Invalidate the text area.*/
1210         if(ta->sel_start > ta->sel_end) {
1211             if(label_data->sel_start != ta->sel_end || label_data->sel_end != ta->sel_start) {
1212                 label_data->sel_start = ta->sel_end;
1213                 label_data->sel_end   = ta->sel_start;
1214                 lv_obj_invalidate(obj);
1215             }
1216         }
1217         else if(ta->sel_start < ta->sel_end) {
1218             if(label_data->sel_start != ta->sel_start || label_data->sel_end != ta->sel_end) {
1219                 label_data->sel_start = ta->sel_start;
1220                 label_data->sel_end   = ta->sel_end;
1221                 lv_obj_invalidate(obj);
1222             }
1223         }
1224         else {
1225             if(label_data->sel_start != LV_DRAW_LABEL_NO_TXT_SEL || label_data->sel_end != LV_DRAW_LABEL_NO_TXT_SEL) {
1226                 label_data->sel_start = LV_DRAW_LABEL_NO_TXT_SEL;
1227                 label_data->sel_end   = LV_DRAW_LABEL_NO_TXT_SEL;
1228                 lv_obj_invalidate(obj);
1229             }
1230         }
1231         /*Finish selection if necessary*/
1232         if(code == LV_EVENT_PRESS_LOST || code == LV_EVENT_RELEASED) {
1233             ta->text_sel_in_prog = 0;
1234         }
1235     }
1236 #else
1237     /*Check if the click happened on the left side of the area outside the label*/
1238     if(rel_pos.x < 0) {
1239         char_id_at_click = 0;
1240     }
1241     /*Check if the click happened on the right side of the area outside the label*/
1242     else if(rel_pos.x >= label_width) {
1243         char_id_at_click = LV_TEXTAREA_CURSOR_LAST;
1244     }
1245     else {
1246         char_id_at_click = lv_label_get_letter_on(ta->label, &rel_pos);
1247     }
1248 
1249     if(code == LV_EVENT_PRESSED) lv_textarea_set_cursor_pos(obj, char_id_at_click);
1250 #endif
1251 }
1252 
insert_handler(lv_obj_t * obj,const char * txt)1253 static lv_res_t insert_handler(lv_obj_t * obj, const char * txt)
1254 {
1255     ta_insert_replace = NULL;
1256     lv_event_send(obj, LV_EVENT_INSERT, (char *)txt);
1257     if(ta_insert_replace) {
1258         if(ta_insert_replace[0] == '\0') return LV_RES_INV; /*Drop this text*/
1259 
1260         /*Add the replaced text directly it's different from the original*/
1261         if(strcmp(ta_insert_replace, txt)) {
1262             lv_textarea_add_text(obj, ta_insert_replace);
1263             return LV_RES_INV;
1264         }
1265     }
1266 
1267     return LV_RES_OK;
1268 }
1269 
draw_placeholder(lv_event_t * e)1270 static void draw_placeholder(lv_event_t * e)
1271 {
1272     lv_obj_t * obj = lv_event_get_target(e);
1273     lv_textarea_t * ta = (lv_textarea_t *)obj;
1274     lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
1275     const char * txt = lv_label_get_text(ta->label);
1276 
1277     /*Draw the place holder*/
1278     if(txt[0] == '\0' && ta->placeholder_txt && ta->placeholder_txt[0] != 0) {
1279         lv_draw_label_dsc_t ph_dsc;
1280         lv_draw_label_dsc_init(&ph_dsc);
1281         lv_obj_init_draw_label_dsc(obj, LV_PART_TEXTAREA_PLACEHOLDER, &ph_dsc);
1282 
1283         if(ta->one_line) ph_dsc.flag |= LV_TEXT_FLAG_EXPAND;
1284 
1285         lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
1286         lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
1287         lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
1288         lv_area_t ph_coords;
1289         lv_area_copy(&ph_coords, &obj->coords);
1290         lv_area_move(&ph_coords, left + border_width, top + border_width);
1291         lv_draw_label(draw_ctx, &ph_dsc, &ph_coords, ta->placeholder_txt, NULL);
1292     }
1293 }
1294 
draw_cursor(lv_event_t * e)1295 static void draw_cursor(lv_event_t * e)
1296 {
1297     lv_obj_t * obj = lv_event_get_target(e);
1298     lv_textarea_t * ta = (lv_textarea_t *)obj;
1299     lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
1300     const char * txt = lv_label_get_text(ta->label);
1301 
1302     if(ta->cursor.show == 0) return;
1303 
1304     lv_draw_rect_dsc_t cur_dsc;
1305     lv_draw_rect_dsc_init(&cur_dsc);
1306     lv_obj_init_draw_rect_dsc(obj, LV_PART_CURSOR, &cur_dsc);
1307 
1308     /*Draw he cursor according to the type*/
1309     lv_area_t cur_area;
1310     lv_area_copy(&cur_area, &ta->cursor.area);
1311 
1312 
1313     cur_area.x1 += ta->label->coords.x1;
1314     cur_area.y1 += ta->label->coords.y1;
1315     cur_area.x2 += ta->label->coords.x1;
1316     cur_area.y2 += ta->label->coords.y1;
1317 
1318     lv_draw_rect(draw_ctx, &cur_dsc, &cur_area);
1319 
1320     lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_CURSOR);
1321     lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_CURSOR) + border_width;
1322     lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_CURSOR) + border_width;
1323     char letter_buf[8] = {0};
1324     lv_memcpy(letter_buf, &txt[ta->cursor.txt_byte_pos], _lv_txt_encoded_size(&txt[ta->cursor.txt_byte_pos]));
1325 
1326     cur_area.x1 += left;
1327     cur_area.y1 += top;
1328 
1329     /*Draw the letter over the cursor only if
1330      *the cursor has background or the letter has different color than the original.
1331      *Else the original letter is drawn twice which makes it look bolder*/
1332     lv_color_t label_color = lv_obj_get_style_text_color(ta->label, 0);
1333     lv_draw_label_dsc_t cur_label_dsc;
1334     lv_draw_label_dsc_init(&cur_label_dsc);
1335     lv_obj_init_draw_label_dsc(obj, LV_PART_CURSOR, &cur_label_dsc);
1336     if(cur_dsc.bg_opa > LV_OPA_MIN || cur_label_dsc.color.full != label_color.full) {
1337         lv_draw_label(draw_ctx, &cur_label_dsc, &cur_area, letter_buf, NULL);
1338     }
1339 }
1340 
1341 #endif
1342