1 /**
2  * @file lv_ta.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_ta.h"
10 #if LV_USE_TA != 0
11 #include <string.h>
12 #include "../lv_core/lv_group.h"
13 #include "../lv_core/lv_refr.h"
14 #include "../lv_draw/lv_draw.h"
15 #include "../lv_themes/lv_theme.h"
16 #include "../lv_misc/lv_anim.h"
17 #include "../lv_misc/lv_txt.h"
18 #include "../lv_misc/lv_math.h"
19 
20 /*********************
21  *      DEFINES
22  *********************/
23 /*Test configuration*/
24 
25 #ifndef LV_TA_DEF_CURSOR_BLINK_TIME
26 #define LV_TA_DEF_CURSOR_BLINK_TIME 400 /*ms*/
27 #endif
28 
29 #ifndef LV_TA_DEF_PWD_SHOW_TIME
30 #define LV_TA_DEF_PWD_SHOW_TIME 1500 /*ms*/
31 #endif
32 
33 #define LV_TA_DEF_WIDTH (2 * LV_DPI)
34 #define LV_TA_DEF_HEIGHT (1 * LV_DPI)
35 
36 /**********************
37  *      TYPEDEFS
38  **********************/
39 
40 /**********************
41  *  STATIC PROTOTYPES
42  **********************/
43 static bool lv_ta_design(lv_obj_t * ta, const lv_area_t * mask, lv_design_mode_t mode);
44 static bool lv_ta_scrollable_design(lv_obj_t * scrl, const lv_area_t * mask, lv_design_mode_t mode);
45 static lv_res_t lv_ta_signal(lv_obj_t * ta, lv_signal_t sign, void * param);
46 static lv_res_t lv_ta_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, void * param);
47 #if LV_USE_ANIMATION
48 static void cursor_blink_anim(lv_obj_t * ta, lv_anim_value_t show);
49 static void pwd_char_hider_anim(lv_obj_t * ta, lv_anim_value_t x);
50 static void pwd_char_hider_anim_ready(lv_anim_t * a);
51 #endif
52 static void pwd_char_hider(lv_obj_t * ta);
53 static bool char_is_accepted(lv_obj_t * ta, uint32_t c);
54 static void get_cursor_style(lv_obj_t * ta, lv_style_t * style_res);
55 static void refr_cursor_area(lv_obj_t * ta);
56 static void placeholder_update(lv_obj_t * ta);
57 static void update_cursor_position_on_click(lv_obj_t * ta, lv_signal_t sign, lv_indev_t * click_source);
58 
59 /**********************
60  *  STATIC VARIABLES
61  **********************/
62 static lv_design_cb_t ancestor_design;
63 static lv_design_cb_t scrl_design;
64 static lv_signal_cb_t ancestor_signal;
65 static lv_signal_cb_t scrl_signal;
66 static const char * ta_insert_replace;
67 
68 /**********************
69  *      MACROS
70  **********************/
71 
72 /**********************
73  *   GLOBAL FUNCTIONS
74  **********************/
75 
76 /**
77  * Create a text area objects
78  * @param par pointer to an object, it will be the parent of the new text area
79  * @param copy pointer to a text area object, if not NULL then the new object will be copied from it
80  * @return pointer to the created text area
81  */
lv_ta_create(lv_obj_t * par,const lv_obj_t * copy)82 lv_obj_t * lv_ta_create(lv_obj_t * par, const lv_obj_t * copy)
83 {
84     LV_LOG_TRACE("text area create started");
85 
86     /*Create the ancestor object*/
87     lv_obj_t * new_ta = lv_page_create(par, copy);
88     lv_mem_assert(new_ta);
89     if(new_ta == NULL) return NULL;
90 
91     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_ta);
92     if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(new_ta);
93     if(scrl_signal == NULL) scrl_signal = lv_obj_get_signal_cb(lv_page_get_scrl(new_ta));
94     if(scrl_design == NULL) scrl_design = lv_obj_get_design_cb(lv_page_get_scrl(new_ta));
95 
96     /*Allocate the object type specific extended data*/
97     lv_ta_ext_t * ext = lv_obj_allocate_ext_attr(new_ta, sizeof(lv_ta_ext_t));
98     lv_mem_assert(ext);
99     if(ext == NULL) return NULL;
100 
101     ext->cursor.state      = 1;
102     ext->pwd_mode          = 0;
103     ext->pwd_tmp           = NULL;
104     ext->pwd_show_time     = LV_TA_DEF_PWD_SHOW_TIME;
105     ext->accapted_chars    = NULL;
106     ext->max_length        = 0;
107     ext->cursor.style      = NULL;
108     ext->cursor.blink_time = LV_TA_DEF_CURSOR_BLINK_TIME;
109     ext->cursor.pos        = 0;
110     ext->cursor.click_pos  = 1;
111     ext->cursor.type       = LV_CURSOR_LINE;
112     ext->cursor.valid_x    = 0;
113     ext->one_line          = 0;
114 #if LV_LABEL_TEXT_SEL
115     ext->text_sel_en = 0;
116 #endif
117     ext->label       = NULL;
118     ext->placeholder = NULL;
119 
120 #if LV_USE_ANIMATION == 0
121     ext->pwd_show_time     = 0;
122     ext->cursor.blink_time = 0;
123 #endif
124 
125     lv_obj_set_signal_cb(new_ta, lv_ta_signal);
126     lv_obj_set_signal_cb(lv_page_get_scrl(new_ta), lv_ta_scrollable_signal);
127     lv_obj_set_design_cb(new_ta, lv_ta_design);
128 
129     /*Init the new text area object*/
130     if(copy == NULL) {
131         lv_page_set_scrl_fit2(new_ta, LV_FIT_FLOOD, LV_FIT_TIGHT);
132 
133         ext->label = lv_label_create(new_ta, NULL);
134 
135         lv_obj_set_design_cb(ext->page.scrl, lv_ta_scrollable_design);
136 
137         lv_label_set_long_mode(ext->label, LV_LABEL_LONG_BREAK);
138         lv_label_set_text(ext->label, "Text area");
139         lv_obj_set_click(ext->label, false);
140         lv_obj_set_size(new_ta, LV_TA_DEF_WIDTH, LV_TA_DEF_HEIGHT);
141         lv_ta_set_sb_mode(new_ta, LV_SB_MODE_DRAG);
142         lv_page_set_style(new_ta, LV_PAGE_STYLE_SCRL, &lv_style_transp_tight);
143 
144         /*Set the default styles*/
145         lv_theme_t * th = lv_theme_get_current();
146         if(th) {
147             lv_ta_set_style(new_ta, LV_TA_STYLE_BG, th->style.ta.area);
148             lv_ta_set_style(new_ta, LV_TA_STYLE_SB, th->style.ta.sb);
149         } else {
150             lv_ta_set_style(new_ta, LV_TA_STYLE_BG, &lv_style_pretty);
151         }
152     }
153     /*Copy an existing object*/
154     else {
155         lv_obj_set_design_cb(ext->page.scrl, lv_ta_scrollable_design);
156         lv_ta_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
157         ext->label             = lv_label_create(new_ta, copy_ext->label);
158         ext->pwd_mode          = copy_ext->pwd_mode;
159         ext->accapted_chars    = copy_ext->accapted_chars;
160         ext->max_length        = copy_ext->max_length;
161         ext->cursor.style      = copy_ext->cursor.style;
162         ext->cursor.pos        = copy_ext->cursor.pos;
163         ext->cursor.valid_x    = copy_ext->cursor.valid_x;
164         ext->cursor.type       = copy_ext->cursor.type;
165         if(copy_ext->one_line) lv_ta_set_one_line(new_ta, true);
166 
167         lv_ta_set_style(new_ta, LV_TA_STYLE_CURSOR, lv_ta_get_style(copy, LV_TA_STYLE_CURSOR));
168 
169         /*Refresh the style with new signal function*/
170         lv_obj_refresh_style(new_ta);
171     }
172 
173 #if LV_USE_ANIMATION
174     if(ext->cursor.blink_time) {
175         /*Create a cursor blinker animation*/
176         lv_anim_t a;
177         a.var            = new_ta;
178         a.exec_cb        = (lv_anim_exec_xcb_t)cursor_blink_anim;
179         a.time           = ext->cursor.blink_time;
180         a.act_time       = 0;
181         a.ready_cb       = NULL;
182         a.start          = 1;
183         a.end            = 0;
184         a.repeat         = 1;
185         a.repeat_pause   = 0;
186         a.playback       = 1;
187         a.playback_pause = 0;
188         a.path_cb        = lv_anim_path_step;
189         lv_anim_create(&a);
190     }
191 #endif
192 
193     LV_LOG_INFO("text area created");
194 
195     return new_ta;
196 }
197 
198 /*======================
199  * Add/remove functions
200  *=====================*/
201 
202 /**
203  * Insert a character to the current cursor position.
204  * To add a wide char, e.g. 'Á' use `lv_txt_encoded_conv_wc('Á')`
205  * @param ta pointer to a text area object
206  * @param c a character (e.g. 'a')
207  */
lv_ta_add_char(lv_obj_t * ta,uint32_t c)208 void lv_ta_add_char(lv_obj_t * ta, uint32_t c)
209 {
210     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
211 
212     uint32_t letter_buf[2];
213     letter_buf[0] = c;
214     letter_buf[1] = '\0';
215 
216     ta_insert_replace = NULL;
217     lv_event_send(ta, LV_EVENT_INSERT, letter_buf);
218     if(ta_insert_replace) {
219         if(ta_insert_replace[0] == '\0') return; /*Drop this text*/
220 
221         /*Add the replaced text directly it's different from the original*/
222         if(strcmp(ta_insert_replace, (char *)letter_buf)) {
223             lv_ta_add_text(ta, ta_insert_replace);
224             return;
225         }
226     }
227 
228     if(ext->one_line && (c == '\n' || c == '\r')) {
229         LV_LOG_INFO("Text area: line break ignored in one-line mode");
230         return;
231     }
232 
233     uint32_t c_uni = lv_txt_encoded_next((const char *)&c, NULL);
234 
235     if(char_is_accepted(ta, c_uni) == false) {
236         LV_LOG_INFO("Character is no accepted by the text area (too long text or not in the "
237                     "accepted list)");
238         return;
239     }
240 
241     /*If a new line was added it shouldn't show edge flash effect*/
242     bool edge_flash_en = lv_ta_get_edge_flash(ta);
243     lv_ta_set_edge_flash(ta, false);
244 
245     if(ext->pwd_mode != 0) pwd_char_hider(ta); /*Make sure all the current text contains only '*'*/
246 
247     lv_label_ins_text(ext->label, ext->cursor.pos, (const char *)letter_buf); /*Insert the character*/
248     lv_ta_clear_selection(ta);                                                /*Clear selection*/
249 
250     if(ext->pwd_mode != 0) {
251 
252         ext->pwd_tmp = lv_mem_realloc(ext->pwd_tmp, strlen(ext->pwd_tmp) + 2); /*+2: the new char + \0 */
253         lv_mem_assert(ext->pwd_tmp);
254         if(ext->pwd_tmp == NULL) return;
255 
256         lv_txt_ins(ext->pwd_tmp, ext->cursor.pos, (const char *)letter_buf);
257 
258 #if LV_USE_ANIMATION
259         /*Auto hide characters*/
260         lv_anim_t a;
261         a.var            = ta;
262         a.exec_cb        = (lv_anim_exec_xcb_t)pwd_char_hider_anim;
263         a.time           = ext->pwd_show_time;
264         a.act_time       = 0;
265         a.ready_cb       = pwd_char_hider_anim_ready;
266         a.start          = 0;
267         a.end            = 1;
268         a.repeat         = 0;
269         a.repeat_pause   = 0;
270         a.playback       = 0;
271         a.playback_pause = 0;
272         a.path_cb        = lv_anim_path_step;
273         lv_anim_create(&a);
274 
275 #else
276         pwd_char_hider(ta);
277 #endif
278     }
279 
280     /*Move the cursor after the new character*/
281     lv_ta_set_cursor_pos(ta, lv_ta_get_cursor_pos(ta) + 1);
282 
283     /*Revert the original edge flash state*/
284     lv_ta_set_edge_flash(ta, edge_flash_en);
285 
286     placeholder_update(ta);
287 
288     lv_event_send(ta, LV_EVENT_VALUE_CHANGED, NULL);
289 }
290 
291 /**
292  * Insert a text to the current cursor position
293  * @param ta pointer to a text area object
294  * @param txt a '\0' terminated string to insert
295  */
lv_ta_add_text(lv_obj_t * ta,const char * txt)296 void lv_ta_add_text(lv_obj_t * ta, const char * txt)
297 {
298     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
299 
300     ta_insert_replace = NULL;
301     lv_event_send(ta, LV_EVENT_INSERT, txt);
302     if(ta_insert_replace) {
303         if(ta_insert_replace[0] == '\0') return; /*Drop this text*/
304 
305         /*Add the replaced text directly it's different from the original*/
306         if(strcmp(ta_insert_replace, txt)) {
307             lv_ta_add_text(ta, ta_insert_replace);
308             return;
309         }
310     }
311 
312     if(ext->pwd_mode != 0) pwd_char_hider(ta); /*Make sure all the current text contains only '*'*/
313 
314     /*Add the character one-by-one if not all characters are accepted or there is character limit.*/
315     if(lv_ta_get_accepted_chars(ta) || lv_ta_get_max_length(ta)) {
316         uint32_t i = 0;
317         while(txt[i] != '\0') {
318             uint32_t c = lv_txt_encoded_next(txt, &i);
319             lv_ta_add_char(ta, lv_txt_unicode_to_encoded(c));
320         }
321         return;
322     }
323 
324     /*If a new line was added it shouldn't show edge flash effect*/
325     bool edge_flash_en = lv_ta_get_edge_flash(ta);
326     lv_ta_set_edge_flash(ta, false);
327 
328     /*Insert the text*/
329     lv_label_ins_text(ext->label, ext->cursor.pos, txt);
330     lv_ta_clear_selection(ta);
331 
332     if(ext->pwd_mode != 0) {
333         ext->pwd_tmp = lv_mem_realloc(ext->pwd_tmp, strlen(ext->pwd_tmp) + strlen(txt) + 1);
334         lv_mem_assert(ext->pwd_tmp);
335         if(ext->pwd_tmp == NULL) return;
336 
337         lv_txt_ins(ext->pwd_tmp, ext->cursor.pos, txt);
338 
339 #if LV_USE_ANIMATION
340         /*Auto hide characters*/
341         lv_anim_t a;
342         a.var            = ta;
343         a.exec_cb        = (lv_anim_exec_xcb_t)pwd_char_hider_anim;
344         a.time           = ext->pwd_show_time;
345         a.act_time       = 0;
346         a.ready_cb       = pwd_char_hider_anim_ready;
347         a.start          = 0;
348         a.end            = 1;
349         a.repeat         = 0;
350         a.repeat_pause   = 0;
351         a.playback       = 0;
352         a.playback_pause = 0;
353         a.path_cb        = lv_anim_path_step;
354         lv_anim_create(&a);
355 #else
356         pwd_char_hider(ta);
357 #endif
358     }
359 
360     /*Move the cursor after the new text*/
361     lv_ta_set_cursor_pos(ta, lv_ta_get_cursor_pos(ta) + lv_txt_get_encoded_length(txt));
362 
363     /*Revert the original edge flash state*/
364     lv_ta_set_edge_flash(ta, edge_flash_en);
365 
366     placeholder_update(ta);
367 
368     lv_event_send(ta, LV_EVENT_VALUE_CHANGED, NULL);
369 }
370 
371 /**
372  * Delete a the left character from the current cursor position
373  * @param ta pointer to a text area object
374  */
lv_ta_del_char(lv_obj_t * ta)375 void lv_ta_del_char(lv_obj_t * ta)
376 {
377     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
378     uint16_t cur_pos  = ext->cursor.pos;
379 
380     if(cur_pos == 0) return;
381 
382     ta_insert_replace = NULL;
383     char del_buf[2]   = {LV_KEY_DEL, '\0'};
384     lv_event_send(ta, LV_EVENT_INSERT, del_buf);
385     if(ta_insert_replace) {
386         if(ta_insert_replace[0] == '\0') return; /*Drop this text*/
387 
388         /*Add the replaced text directly it's different from the original*/
389         if(strcmp(ta_insert_replace, del_buf)) {
390             lv_ta_add_text(ta, ta_insert_replace);
391             return;
392         }
393     }
394 
395     char * label_txt = lv_label_get_text(ext->label);
396     /*Delete a character*/
397     lv_txt_cut(label_txt, ext->cursor.pos - 1, 1);
398     /*Refresh the label*/
399     lv_label_set_text(ext->label, label_txt);
400     lv_ta_clear_selection(ta);
401 
402     /*Don't let 'width == 0' because cursor will not be visible*/
403     if(lv_obj_get_width(ext->label) == 0) {
404         const lv_style_t * style = lv_obj_get_style(ext->label);
405         lv_obj_set_width(ext->label, style->line.width);
406     }
407 
408     if(ext->pwd_mode != 0) {
409         uint32_t byte_pos = lv_txt_encoded_get_byte_id(ext->pwd_tmp, ext->cursor.pos - 1);
410         lv_txt_cut(ext->pwd_tmp, ext->cursor.pos - 1, lv_txt_encoded_size(&label_txt[byte_pos]));
411 
412         ext->pwd_tmp = lv_mem_realloc(ext->pwd_tmp, strlen(ext->pwd_tmp) + 1);
413         lv_mem_assert(ext->pwd_tmp);
414         if(ext->pwd_tmp == NULL) return;
415     }
416 
417     /*Move the cursor to the place of the deleted character*/
418     lv_ta_set_cursor_pos(ta, ext->cursor.pos - 1);
419 
420     placeholder_update(ta);
421 
422     lv_event_send(ta, LV_EVENT_VALUE_CHANGED, NULL);
423 }
424 
425 /**
426  * Delete the right character from the current cursor position
427  * @param ta pointer to a text area object
428  */
lv_ta_del_char_forward(lv_obj_t * ta)429 void lv_ta_del_char_forward(lv_obj_t * ta)
430 {
431     uint16_t cp = lv_ta_get_cursor_pos(ta);
432     lv_ta_set_cursor_pos(ta, cp + 1);
433     if(cp != lv_ta_get_cursor_pos(ta)) lv_ta_del_char(ta);
434 }
435 
436 /*=====================
437  * Setter functions
438  *====================*/
439 
440 /**
441  * Set the text of a text area
442  * @param ta pointer to a text area
443  * @param txt pointer to the text
444  */
lv_ta_set_text(lv_obj_t * ta,const char * txt)445 void lv_ta_set_text(lv_obj_t * ta, const char * txt)
446 {
447     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
448 
449     /*Clear the existing selection*/
450     lv_ta_clear_selection(ta);
451 
452     /*Add the character one-by-one if not all characters are accepted or there is character limit.*/
453     if(lv_ta_get_accepted_chars(ta) || lv_ta_get_max_length(ta)) {
454         lv_label_set_text(ext->label, "");
455         lv_ta_set_cursor_pos(ta, LV_TA_CURSOR_LAST);
456 
457         uint32_t i = 0;
458         while(txt[i] != '\0') {
459             uint32_t c = lv_txt_encoded_next(txt, &i);
460             lv_ta_add_char(ta, lv_txt_unicode_to_encoded(c));
461         }
462     } else {
463         lv_label_set_text(ext->label, txt);
464         lv_ta_set_cursor_pos(ta, LV_TA_CURSOR_LAST);
465     }
466 
467     /*Don't let 'width == 0' because the cursor will not be visible*/
468     if(lv_obj_get_width(ext->label) == 0) {
469         const lv_style_t * style = lv_obj_get_style(ext->label);
470         lv_obj_set_width(ext->label, lv_font_get_glyph_width(style->text.font, ' ', '\0'));
471     }
472 
473     if(ext->pwd_mode != 0) {
474         ext->pwd_tmp = lv_mem_realloc(ext->pwd_tmp, strlen(txt) + 1);
475         lv_mem_assert(ext->pwd_tmp);
476         if(ext->pwd_tmp == NULL) return;
477         strcpy(ext->pwd_tmp, txt);
478 
479 #if LV_USE_ANIMATION
480         /*Auto hide characters*/
481         lv_anim_t a;
482         a.var            = ta;
483         a.exec_cb        = (lv_anim_exec_xcb_t)pwd_char_hider_anim;
484         a.time           = ext->pwd_show_time;
485         a.act_time       = 0;
486         a.ready_cb       = pwd_char_hider_anim_ready;
487         a.start          = 0;
488         a.end            = 1;
489         a.repeat         = 0;
490         a.repeat_pause   = 0;
491         a.playback       = 0;
492         a.playback_pause = 0;
493         a.path_cb        = lv_anim_path_step;
494         lv_anim_create(&a);
495 #else
496         pwd_char_hider(ta);
497 #endif
498     }
499 
500     placeholder_update(ta);
501 
502     lv_event_send(ta, LV_EVENT_VALUE_CHANGED, NULL);
503 }
504 
505 /**
506  * Set the placeholder text of a text area
507  * @param ta pointer to a text area
508  * @param txt pointer to the text
509  */
lv_ta_set_placeholder_text(lv_obj_t * ta,const char * txt)510 void lv_ta_set_placeholder_text(lv_obj_t * ta, const char * txt)
511 {
512     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
513 
514     /*Create the placeholder label only when it is needed*/
515     if(ext->placeholder == NULL) {
516         ext->placeholder = lv_label_create(ta, NULL);
517 
518         if(ext->one_line) {
519             lv_label_set_long_mode(ext->placeholder, LV_LABEL_LONG_EXPAND);
520         } else {
521             lv_label_set_long_mode(ext->placeholder, LV_LABEL_LONG_BREAK);
522         }
523     }
524 
525     lv_label_set_text(ext->placeholder, txt);
526 
527     placeholder_update(ta);
528 }
529 
530 /**
531  * Set the cursor position
532  * @param obj pointer to a text area object
533  * @param pos the new cursor position in character index
534  *             < 0 : index from the end of the text
535  *             LV_TA_CURSOR_LAST: go after the last character
536  */
lv_ta_set_cursor_pos(lv_obj_t * ta,int16_t pos)537 void lv_ta_set_cursor_pos(lv_obj_t * ta, int16_t pos)
538 {
539     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
540     if(ext->cursor.pos == pos) return;
541 
542     uint16_t len = lv_txt_get_encoded_length(lv_label_get_text(ext->label));
543 
544     if(pos < 0) pos = len + pos;
545 
546     if(pos > len || pos == LV_TA_CURSOR_LAST) pos = len;
547 
548     ext->cursor.pos = pos;
549 
550     /*Position the label to make the cursor visible*/
551     lv_obj_t * label_par = lv_obj_get_parent(ext->label);
552     lv_point_t cur_pos;
553     const lv_style_t * style = lv_obj_get_style(ta);
554     const lv_font_t * font_p = style->text.font;
555     lv_area_t label_cords;
556     lv_area_t ta_cords;
557     lv_label_get_letter_pos(ext->label, pos, &cur_pos);
558     lv_obj_get_coords(ta, &ta_cords);
559     lv_obj_get_coords(ext->label, &label_cords);
560 
561     /*Check the top*/
562     lv_coord_t font_h = lv_font_get_line_height(font_p);
563     if(lv_obj_get_y(label_par) + cur_pos.y < 0) {
564         lv_obj_set_y(label_par, -cur_pos.y + style->body.padding.top);
565     }
566 
567     /*Check the bottom*/
568     if(label_cords.y1 + cur_pos.y + font_h + style->body.padding.bottom > ta_cords.y2) {
569         lv_obj_set_y(label_par, -(cur_pos.y - lv_obj_get_height(ta) + font_h + style->body.padding.top +
570                                   style->body.padding.bottom));
571     }
572     /*Check the left (use the font_h as general unit)*/
573     if(lv_obj_get_x(label_par) + cur_pos.x < font_h) {
574         lv_obj_set_x(label_par, -cur_pos.x + font_h);
575     }
576 
577     /*Check the right (use the font_h as general unit)*/
578     if(label_cords.x1 + cur_pos.x + font_h + style->body.padding.right > ta_cords.x2) {
579         lv_obj_set_x(label_par, -(cur_pos.x - lv_obj_get_width(ta) + font_h + style->body.padding.left +
580                                   style->body.padding.right));
581     }
582 
583     ext->cursor.valid_x = cur_pos.x;
584 
585 #if LV_USE_ANIMATION
586     if(ext->cursor.blink_time) {
587         /*Reset cursor blink animation*/
588         lv_anim_t a;
589         a.var            = ta;
590         a.exec_cb        = (lv_anim_exec_xcb_t)cursor_blink_anim;
591         a.time           = ext->cursor.blink_time;
592         a.act_time       = 0;
593         a.ready_cb       = NULL;
594         a.start          = 1;
595         a.end            = 0;
596         a.repeat         = 1;
597         a.repeat_pause   = 0;
598         a.playback       = 1;
599         a.playback_pause = 0;
600         a.path_cb        = lv_anim_path_step;
601         lv_anim_create(&a);
602     }
603 #endif
604 
605     refr_cursor_area(ta);
606 }
607 
608 /**
609  * Set the cursor type.
610  * @param ta pointer to a text area object
611  * @param cur_type: element of 'lv_ta_cursor_type_t'
612  */
lv_ta_set_cursor_type(lv_obj_t * ta,lv_cursor_type_t cur_type)613 void lv_ta_set_cursor_type(lv_obj_t * ta, lv_cursor_type_t cur_type)
614 {
615     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
616     if(ext->cursor.type == cur_type) return;
617 
618     ext->cursor.type = cur_type;
619 
620     refr_cursor_area(ta);
621 }
622 
623 /**
624  * Enable/Disable the positioning of the the cursor by clicking the text on the text area.
625  * @param ta pointer to a text area object
626  * @param en true: enable click positions; false: disable
627  */
lv_ta_set_cursor_click_pos(lv_obj_t * ta,bool en)628 void lv_ta_set_cursor_click_pos(lv_obj_t * ta, bool en)
629 {
630     lv_ta_ext_t * ext     = lv_obj_get_ext_attr(ta);
631     ext->cursor.click_pos = en ? 1 : 0;
632 }
633 
634 /**
635  * Enable/Disable password mode
636  * @param ta pointer to a text area object
637  * @param en true: enable, false: disable
638  */
lv_ta_set_pwd_mode(lv_obj_t * ta,bool en)639 void lv_ta_set_pwd_mode(lv_obj_t * ta, bool en)
640 {
641     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
642     if(ext->pwd_mode == en) return;
643 
644     /*Pwd mode is now enabled*/
645     if(ext->pwd_mode == 0 && en != false) {
646         char * txt   = lv_label_get_text(ext->label);
647         uint16_t len = strlen(txt);
648         ext->pwd_tmp = lv_mem_alloc(len + 1);
649         lv_mem_assert(ext->pwd_tmp);
650         if(ext->pwd_tmp == NULL) return;
651 
652         strcpy(ext->pwd_tmp, txt);
653 
654         uint16_t i;
655         for(i = 0; i < len; i++) {
656             txt[i] = '*'; /*All char to '*'*/
657         }
658         txt[i] = '\0';
659 
660         lv_ta_clear_selection(ta);
661 
662         lv_label_set_text(ext->label, NULL);
663     }
664     /*Pwd mode is now disabled*/
665     else if(ext->pwd_mode == 1 && en == false) {
666         lv_ta_clear_selection(ta);
667         lv_label_set_text(ext->label, ext->pwd_tmp);
668         lv_mem_free(ext->pwd_tmp);
669         ext->pwd_tmp = NULL;
670     }
671 
672     ext->pwd_mode = en == false ? 0 : 1;
673 
674     refr_cursor_area(ta);
675 }
676 
677 /**
678  * Configure the text area to one line or back to normal
679  * @param ta pointer to a Text area object
680  * @param en true: one line, false: normal
681  */
lv_ta_set_one_line(lv_obj_t * ta,bool en)682 void lv_ta_set_one_line(lv_obj_t * ta, bool en)
683 {
684     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
685     if(ext->one_line == en) return;
686 
687     if(en) {
688         const lv_style_t * style_ta    = lv_obj_get_style(ta);
689         const lv_style_t * style_scrl  = lv_obj_get_style(lv_page_get_scrl(ta));
690         const lv_style_t * style_label = lv_obj_get_style(ext->label);
691         lv_coord_t font_h              = lv_font_get_line_height(style_label->text.font);
692 
693         ext->one_line = 1;
694         lv_page_set_scrl_fit2(ta, LV_FIT_TIGHT, LV_FIT_FLOOD);
695         lv_obj_set_height(ta, font_h + style_ta->body.padding.top + style_ta->body.padding.bottom +
696                                   style_scrl->body.padding.top + style_scrl->body.padding.bottom);
697         lv_label_set_long_mode(ext->label, LV_LABEL_LONG_EXPAND);
698         if(ext->placeholder) lv_label_set_long_mode(ext->placeholder, LV_LABEL_LONG_EXPAND);
699         lv_obj_set_pos(lv_page_get_scrl(ta), style_ta->body.padding.left, style_ta->body.padding.top);
700     } else {
701         const lv_style_t * style_ta = lv_obj_get_style(ta);
702 
703         ext->one_line = 0;
704         lv_page_set_scrl_fit2(ta, LV_FIT_FLOOD, LV_FIT_TIGHT);
705         lv_label_set_long_mode(ext->label, LV_LABEL_LONG_BREAK);
706         if(ext->placeholder) lv_label_set_long_mode(ext->placeholder, LV_LABEL_LONG_BREAK);
707 
708         lv_obj_set_height(ta, LV_TA_DEF_HEIGHT);
709         lv_obj_set_pos(lv_page_get_scrl(ta), style_ta->body.padding.left, style_ta->body.padding.top);
710     }
711 
712     placeholder_update(ta);
713     refr_cursor_area(ta);
714 }
715 
716 /**
717  * Set the alignment of the text area.
718  * In one line mode the text can be scrolled only with `LV_LABEL_ALIGN_LEFT`.
719  * This function should be called if the size of text area changes.
720  * @param ta pointer to a text are object
721  * @param align the desired alignment from `lv_label_align_t`. (LV_LABEL_ALIGN_LEFT/CENTER/RIGHT)
722  */
lv_ta_set_text_align(lv_obj_t * ta,lv_label_align_t align)723 void lv_ta_set_text_align(lv_obj_t * ta, lv_label_align_t align)
724 {
725     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
726     lv_obj_t * label  = lv_ta_get_label(ta);
727     if(!ext->one_line) {
728         lv_label_set_align(label, align);
729     } else {
730         /*Normal left align. Just let the text expand*/
731         if(align == LV_LABEL_ALIGN_LEFT) {
732             lv_label_set_long_mode(label, LV_LABEL_LONG_EXPAND);
733             lv_page_set_scrl_fit2(ta, LV_FIT_TIGHT, LV_FIT_FLOOD);
734             lv_label_set_align(label, align);
735 
736         }
737         /*Else use fix label width equal to the Text area width*/
738         else {
739             lv_label_set_long_mode(label, LV_LABEL_LONG_CROP);
740             lv_page_set_scrl_fit2(ta, LV_FIT_FLOOD, LV_FIT_FLOOD);
741             lv_label_set_align(label, align);
742 
743             lv_obj_set_width(label, lv_page_get_fit_width(ta));
744         }
745     }
746 
747     refr_cursor_area(ta);
748 }
749 
750 /**
751  * Set a list of characters. Only these characters will be accepted by the text area
752  * @param ta pointer to  Text Area
753  * @param list list of characters. Only the pointer is saved. E.g. "+-.,0123456789"
754  */
lv_ta_set_accepted_chars(lv_obj_t * ta,const char * list)755 void lv_ta_set_accepted_chars(lv_obj_t * ta, const char * list)
756 {
757     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
758 
759     ext->accapted_chars = list;
760 }
761 
762 /**
763  * Set max length of a Text Area.
764  * @param ta pointer to  Text Area
765  * @param num the maximal number of characters can be added (`lv_ta_set_text` ignores it)
766  */
lv_ta_set_max_length(lv_obj_t * ta,uint16_t num)767 void lv_ta_set_max_length(lv_obj_t * ta, uint16_t num)
768 {
769     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
770 
771     ext->max_length = num;
772 }
773 
774 /**
775  * In `LV_EVENT_INSERT` the text which planned to be inserted can be replaced by an other text.
776  * It can be used to add automatic formatting to the text area.
777  * @param ta pointer to a text area.
778  * @param txt pointer to a new string to insert. If `""` no text will be added.
779  *            The variable must be live after the `event_cb` exists. (Should be `global` or
780  * `static`)
781  */
lv_ta_set_insert_replace(lv_obj_t * ta,const char * txt)782 void lv_ta_set_insert_replace(lv_obj_t * ta, const char * txt)
783 {
784     (void)ta; /*Unused*/
785     ta_insert_replace = txt;
786 }
787 
788 /**
789  * Set a style of a text area
790  * @param ta pointer to a text area object
791  * @param type which style should be set
792  * @param style pointer to a style
793  */
lv_ta_set_style(lv_obj_t * ta,lv_ta_style_t type,const lv_style_t * style)794 void lv_ta_set_style(lv_obj_t * ta, lv_ta_style_t type, const lv_style_t * style)
795 {
796     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
797 
798     switch(type) {
799         case LV_TA_STYLE_BG: lv_page_set_style(ta, LV_PAGE_STYLE_BG, style); break;
800         case LV_TA_STYLE_SB: lv_page_set_style(ta, LV_PAGE_STYLE_SB, style); break;
801         case LV_TA_STYLE_EDGE_FLASH: lv_page_set_style(ta, LV_PAGE_STYLE_EDGE_FLASH, style); break;
802         case LV_TA_STYLE_CURSOR:
803             ext->cursor.style = style;
804             lv_obj_refresh_ext_draw_pad(lv_page_get_scrl(ta)); /*Refresh ext. size because of cursor drawing*/
805             refr_cursor_area(ta);
806             break;
807         case LV_TA_STYLE_PLACEHOLDER:
808             if(ext->placeholder) lv_label_set_style(ext->placeholder, LV_LABEL_STYLE_MAIN, style);
809             break;
810     }
811 }
812 
813 /**
814  * Enable/disable selection mode.
815  * @param ta pointer to a text area object
816  * @param en true or false to enable/disable selection mode
817  */
lv_ta_set_text_sel(lv_obj_t * ta,bool en)818 void lv_ta_set_text_sel(lv_obj_t * ta, bool en)
819 {
820 #if LV_LABEL_TEXT_SEL
821     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
822 
823     ext->text_sel_en = en;
824 
825     if(!en) lv_ta_clear_selection(ta);
826 #else
827     (void)ta; /*Unused*/
828     (void)en; /*Unused*/
829 #endif
830 }
831 
832 /**
833  * Set how long show the password before changing it to '*'
834  * @param ta pointer to Text area
835  * @param time show time in milliseconds. 0: hide immediately.
836  */
lv_ta_set_pwd_show_time(lv_obj_t * ta,uint16_t time)837 void lv_ta_set_pwd_show_time(lv_obj_t * ta, uint16_t time)
838 {
839 #if LV_USE_ANIMATION == 0
840     time = 0;
841 #endif
842 
843     lv_ta_ext_t * ext  = lv_obj_get_ext_attr(ta);
844     ext->pwd_show_time = time;
845 }
846 
847 /**
848  * Set cursor blink animation time
849  * @param ta pointer to Text area
850  * @param time blink period. 0: disable blinking
851  */
lv_ta_set_cursor_blink_time(lv_obj_t * ta,uint16_t time)852 void lv_ta_set_cursor_blink_time(lv_obj_t * ta, uint16_t time)
853 {
854 #if LV_USE_ANIMATION == 0
855     time = 0;
856 #endif
857 
858     lv_ta_ext_t * ext      = lv_obj_get_ext_attr(ta);
859     ext->cursor.blink_time = time;
860 
861 #if LV_USE_ANIMATION
862     if(ext->cursor.blink_time) {
863         /*Reset cursor blink animation*/
864         lv_anim_t a;
865         a.var            = ta;
866         a.exec_cb        = (lv_anim_exec_xcb_t)cursor_blink_anim;
867         a.time           = ext->cursor.blink_time;
868         a.act_time       = 0;
869         a.ready_cb       = NULL;
870         a.start          = 1;
871         a.end            = 0;
872         a.repeat         = 1;
873         a.repeat_pause   = 0;
874         a.playback       = 1;
875         a.playback_pause = 0;
876         a.path_cb        = lv_anim_path_step;
877         lv_anim_create(&a);
878     } else {
879         ext->cursor.state = 1;
880     }
881 #else
882     ext->cursor.state = 1;
883 #endif
884 }
885 
886 /*=====================
887  * Getter functions
888  *====================*/
889 
890 /**
891  * Get the text of a text area. In password mode it gives the real text (not '*'s).
892  * @param ta pointer to a text area object
893  * @return pointer to the text
894  */
lv_ta_get_text(const lv_obj_t * ta)895 const char * lv_ta_get_text(const lv_obj_t * ta)
896 {
897     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
898 
899     const char * txt;
900     if(ext->pwd_mode == 0) {
901         txt = lv_label_get_text(ext->label);
902     } else {
903         txt = ext->pwd_tmp;
904     }
905 
906     return txt;
907 }
908 
909 /**
910  * Get the placeholder text of a text area
911  * @param ta pointer to a text area object
912  * @return pointer to the text
913  */
lv_ta_get_placeholder_text(lv_obj_t * ta)914 const char * lv_ta_get_placeholder_text(lv_obj_t * ta)
915 {
916     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
917 
918     const char * txt = NULL;
919 
920     if(ext->placeholder) txt = lv_label_get_text(ext->label);
921 
922     return txt;
923 }
924 
925 /**
926  * Get the label of a text area
927  * @param ta pointer to a text area object
928  * @return pointer to the label object
929  */
lv_ta_get_label(const lv_obj_t * ta)930 lv_obj_t * lv_ta_get_label(const lv_obj_t * ta)
931 {
932     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
933     return ext->label;
934 }
935 
936 /**
937  * Get the current cursor position in character index
938  * @param ta pointer to a text area object
939  * @return the cursor position
940  */
lv_ta_get_cursor_pos(const lv_obj_t * ta)941 uint16_t lv_ta_get_cursor_pos(const lv_obj_t * ta)
942 {
943     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
944     return ext->cursor.pos;
945 }
946 
947 /**
948  * Get the current cursor type.
949  * @param ta pointer to a text area object
950  * @return element of 'lv_ta_cursor_type_t'
951  */
lv_ta_get_cursor_type(const lv_obj_t * ta)952 lv_cursor_type_t lv_ta_get_cursor_type(const lv_obj_t * ta)
953 {
954     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
955     return ext->cursor.type;
956 }
957 
958 /**
959  * Get whether the cursor click positioning is enabled or not.
960  * @param ta pointer to a text area object
961  * @return true: enable click positions; false: disable
962  */
lv_ta_get_cursor_click_pos(lv_obj_t * ta)963 bool lv_ta_get_cursor_click_pos(lv_obj_t * ta)
964 {
965     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
966     return ext->cursor.click_pos ? true : false;
967 }
968 
969 /**
970  * Get the password mode attribute
971  * @param ta pointer to a text area object
972  * @return true: password mode is enabled, false: disabled
973  */
lv_ta_get_pwd_mode(const lv_obj_t * ta)974 bool lv_ta_get_pwd_mode(const lv_obj_t * ta)
975 {
976     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
977     return ext->pwd_mode == 0 ? false : true;
978 }
979 
980 /**
981  * Get the one line configuration attribute
982  * @param ta pointer to a text area object
983  * @return true: one line configuration is enabled, false: disabled
984  */
lv_ta_get_one_line(const lv_obj_t * ta)985 bool lv_ta_get_one_line(const lv_obj_t * ta)
986 {
987     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
988     return ext->one_line == 0 ? false : true;
989 }
990 
991 /**
992  * Get a list of accepted characters.
993  * @param ta pointer to  Text Area
994  * @return list of accented characters.
995  */
lv_ta_get_accepted_chars(lv_obj_t * ta)996 const char * lv_ta_get_accepted_chars(lv_obj_t * ta)
997 {
998     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
999 
1000     return ext->accapted_chars;
1001 }
1002 
1003 /**
1004  * Set max length of a Text Area.
1005  * @param ta pointer to  Text Area
1006  * @return the maximal number of characters to be add
1007  */
lv_ta_get_max_length(lv_obj_t * ta)1008 uint16_t lv_ta_get_max_length(lv_obj_t * ta)
1009 {
1010     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1011     return ext->max_length;
1012 }
1013 
1014 /**
1015  * Get a style of a text area
1016  * @param ta pointer to a text area object
1017  * @param type which style should be get
1018  * @return style pointer to a style
1019  */
lv_ta_get_style(const lv_obj_t * ta,lv_ta_style_t type)1020 const lv_style_t * lv_ta_get_style(const lv_obj_t * ta, lv_ta_style_t type)
1021 {
1022     const lv_style_t * style = NULL;
1023     lv_ta_ext_t * ext        = lv_obj_get_ext_attr(ta);
1024 
1025     switch(type) {
1026         case LV_TA_STYLE_BG: style = lv_page_get_style(ta, LV_PAGE_STYLE_BG); break;
1027         case LV_TA_STYLE_SB: style = lv_page_get_style(ta, LV_PAGE_STYLE_SB); break;
1028         case LV_TA_STYLE_EDGE_FLASH: style = lv_page_get_style(ta, LV_PAGE_STYLE_EDGE_FLASH); break;
1029         case LV_TA_STYLE_CURSOR: style = ext->cursor.style; break;
1030         case LV_TA_STYLE_PLACEHOLDER:
1031             if(ext->placeholder) style = lv_label_get_style(ext->placeholder, LV_LABEL_STYLE_MAIN);
1032             break;
1033         default: style = NULL; break;
1034     }
1035 
1036     return style;
1037 }
1038 
1039 /**
1040  * Find whether text is selected or not.
1041  * @param ta Text area object
1042  * @return whether text is selected or not
1043  */
lv_ta_text_is_selected(const lv_obj_t * ta)1044 bool lv_ta_text_is_selected(const lv_obj_t * ta)
1045 {
1046 #if LV_LABEL_TEXT_SEL
1047     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1048 
1049     if((lv_label_get_text_sel_start(ext->label) == LV_LABEL_TEXT_SEL_OFF ||
1050         lv_label_get_text_sel_end(ext->label) == LV_LABEL_TEXT_SEL_OFF)) {
1051         return true;
1052     } else {
1053         return false;
1054     }
1055 #else
1056     (void)ta; /*Unused*/
1057     return false;
1058 #endif
1059 }
1060 
1061 /**
1062  * Find whether selection mode is enabled.
1063  * @param ta pointer to a text area object
1064  * @return true: selection mode is enabled, false: disabled
1065  */
lv_ta_get_text_sel_en(lv_obj_t * ta)1066 bool lv_ta_get_text_sel_en(lv_obj_t * ta)
1067 {
1068 #if LV_LABEL_TEXT_SEL
1069     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1070     return ext->text_sel_en;
1071 #else
1072     (void)ta; /*Unused*/
1073     return false;
1074 #endif
1075 }
1076 
1077 /**
1078  * Set how long show the password before changing it to '*'
1079  * @param ta pointer to Text area
1080  * @return show time in milliseconds. 0: hide immediately.
1081  */
lv_ta_get_pwd_show_time(lv_obj_t * ta)1082 uint16_t lv_ta_get_pwd_show_time(lv_obj_t * ta)
1083 {
1084     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1085 
1086     return ext->pwd_show_time;
1087 }
1088 
1089 /**
1090  * Set cursor blink animation time
1091  * @param ta pointer to Text area
1092  * @return time blink period. 0: disable blinking
1093  */
lv_ta_get_cursor_blink_time(lv_obj_t * ta)1094 uint16_t lv_ta_get_cursor_blink_time(lv_obj_t * ta)
1095 {
1096     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1097     return ext->cursor.blink_time;
1098 }
1099 
1100 /*=====================
1101  * Other functions
1102  *====================*/
1103 
1104 /**
1105  * Clear the selection on the text area.
1106  * @param ta Text area object
1107  */
lv_ta_clear_selection(lv_obj_t * ta)1108 void lv_ta_clear_selection(lv_obj_t * ta)
1109 {
1110 #if LV_LABEL_TEXT_SEL
1111     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1112 
1113     if(lv_label_get_text_sel_start(ext->label) != LV_LABEL_TEXT_SEL_OFF ||
1114        lv_label_get_text_sel_end(ext->label) != LV_LABEL_TEXT_SEL_OFF) {
1115         lv_label_set_text_sel_start(ext->label, LV_LABEL_TEXT_SEL_OFF);
1116         lv_label_set_text_sel_end(ext->label, LV_LABEL_TEXT_SEL_OFF);
1117     }
1118 #else
1119     (void)ta; /*Unused*/
1120 #endif
1121 }
1122 
1123 /**
1124  * Move the cursor one character right
1125  * @param ta pointer to a text area object
1126  */
lv_ta_cursor_right(lv_obj_t * ta)1127 void lv_ta_cursor_right(lv_obj_t * ta)
1128 {
1129     uint16_t cp = lv_ta_get_cursor_pos(ta);
1130     cp++;
1131     lv_ta_set_cursor_pos(ta, cp);
1132 }
1133 
1134 /**
1135  * Move the cursor one character left
1136  * @param ta pointer to a text area object
1137  */
lv_ta_cursor_left(lv_obj_t * ta)1138 void lv_ta_cursor_left(lv_obj_t * ta)
1139 {
1140     uint16_t cp = lv_ta_get_cursor_pos(ta);
1141     if(cp > 0) {
1142         cp--;
1143         lv_ta_set_cursor_pos(ta, cp);
1144     }
1145 }
1146 
1147 /**
1148  * Move the cursor one line down
1149  * @param ta pointer to a text area object
1150  */
lv_ta_cursor_down(lv_obj_t * ta)1151 void lv_ta_cursor_down(lv_obj_t * ta)
1152 {
1153     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1154     lv_point_t pos;
1155 
1156     /*Get the position of the current letter*/
1157     lv_label_get_letter_pos(ext->label, lv_ta_get_cursor_pos(ta), &pos);
1158 
1159     /*Increment the y with one line and keep the valid x*/
1160     const lv_style_t * label_style = lv_obj_get_style(ext->label);
1161     const lv_font_t * font_p       = label_style->text.font;
1162     lv_coord_t font_h              = lv_font_get_line_height(font_p);
1163     pos.y += font_h + label_style->text.line_space + 1;
1164     pos.x = ext->cursor.valid_x;
1165 
1166     /*Do not go below the last line*/
1167     if(pos.y < lv_obj_get_height(ext->label)) {
1168         /*Get the letter index on the new cursor position and set it*/
1169         uint16_t new_cur_pos = lv_label_get_letter_on(ext->label, &pos);
1170 
1171         lv_coord_t cur_valid_x_tmp = ext->cursor.valid_x; /*Cursor position set overwrites the valid positon */
1172         lv_ta_set_cursor_pos(ta, new_cur_pos);
1173         ext->cursor.valid_x = cur_valid_x_tmp;
1174     }
1175 }
1176 
1177 /**
1178  * Move the cursor one line up
1179  * @param ta pointer to a text area object
1180  */
lv_ta_cursor_up(lv_obj_t * ta)1181 void lv_ta_cursor_up(lv_obj_t * ta)
1182 {
1183     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1184     lv_point_t pos;
1185 
1186     /*Get the position of the current letter*/
1187     lv_label_get_letter_pos(ext->label, lv_ta_get_cursor_pos(ta), &pos);
1188 
1189     /*Decrement the y with one line and keep the valid x*/
1190     const lv_style_t * label_style = lv_obj_get_style(ext->label);
1191     const lv_font_t * font         = label_style->text.font;
1192     lv_coord_t font_h              = lv_font_get_line_height(font);
1193     pos.y -= font_h + label_style->text.line_space - 1;
1194     pos.x = ext->cursor.valid_x;
1195 
1196     /*Get the letter index on the new cursor position and set it*/
1197     uint16_t new_cur_pos       = lv_label_get_letter_on(ext->label, &pos);
1198     lv_coord_t cur_valid_x_tmp = ext->cursor.valid_x; /*Cursor position set overwrites the valid positon */
1199     lv_ta_set_cursor_pos(ta, new_cur_pos);
1200     ext->cursor.valid_x = cur_valid_x_tmp;
1201 }
1202 
1203 /**********************
1204  *   STATIC FUNCTIONS
1205  **********************/
1206 
1207 /**
1208  * Handle the drawing related tasks of the text areas
1209  * @param ta pointer to an object
1210  * @param mask the object will be drawn only in this area
1211  * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
1212  *                                  (return 'true' if yes)
1213  *             LV_DESIGN_DRAW_MAIN: draw the object (always return 'true')
1214  *             LV_DESIGN_DRAW_POST: drawing after every children are drawn
1215  * @param return true/false, depends on 'mode'
1216  */
lv_ta_design(lv_obj_t * ta,const lv_area_t * mask,lv_design_mode_t mode)1217 static bool lv_ta_design(lv_obj_t * ta, const lv_area_t * mask, lv_design_mode_t mode)
1218 {
1219     if(mode == LV_DESIGN_COVER_CHK) {
1220         /*Return false if the object is not covers the mask_p area*/
1221         return ancestor_design(ta, mask, mode);
1222     } else if(mode == LV_DESIGN_DRAW_MAIN) {
1223         /*Draw the object*/
1224         ancestor_design(ta, mask, mode);
1225 
1226     } else if(mode == LV_DESIGN_DRAW_POST) {
1227         ancestor_design(ta, mask, mode);
1228     }
1229     return true;
1230 }
1231 
1232 /**
1233  * An extended scrollable design of the page. Calls the normal design function and draws a cursor.
1234  * @param scrl pointer to the scrollable part of the Text area
1235  * @param mask  the object will be drawn only in this area
1236  * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
1237  *                                  (return 'true' if yes)
1238  *             LV_DESIGN_DRAW_MAIN: draw the object (always return 'true')
1239  *             LV_DESIGN_DRAW_POST: drawing after every children are drawn
1240  * @return return true/false, depends on 'mode'
1241  */
lv_ta_scrollable_design(lv_obj_t * scrl,const lv_area_t * mask,lv_design_mode_t mode)1242 static bool lv_ta_scrollable_design(lv_obj_t * scrl, const lv_area_t * mask, lv_design_mode_t mode)
1243 {
1244     if(mode == LV_DESIGN_COVER_CHK) {
1245         /*Return false if the object is not covers the mask_p area*/
1246         return scrl_design(scrl, mask, mode);
1247     } else if(mode == LV_DESIGN_DRAW_MAIN) {
1248         /*Draw the object*/
1249         scrl_design(scrl, mask, mode);
1250     } else if(mode == LV_DESIGN_DRAW_POST) {
1251         scrl_design(scrl, mask, mode);
1252 
1253         /*Draw the cursor*/
1254         lv_obj_t * ta     = lv_obj_get_parent(scrl);
1255         lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1256 
1257         if(ext->cursor.type == LV_CURSOR_NONE || (ext->cursor.type & LV_CURSOR_HIDDEN) || ext->cursor.state == 0) {
1258             return true; /*The cursor is not visible now*/
1259         }
1260 
1261         lv_style_t cur_style;
1262         get_cursor_style(ta, &cur_style);
1263 
1264         const char * txt = lv_label_get_text(ext->label);
1265 
1266         /*Draw he cursor according to the type*/
1267         lv_area_t cur_area;
1268         lv_area_copy(&cur_area, &ext->cursor.area);
1269 
1270         cur_area.x1 += ext->label->coords.x1;
1271         cur_area.y1 += ext->label->coords.y1;
1272         cur_area.x2 += ext->label->coords.x1;
1273         cur_area.y2 += ext->label->coords.y1;
1274 
1275         lv_opa_t opa_scale = lv_obj_get_opa_scale(ta);
1276 
1277         if(ext->cursor.type == LV_CURSOR_LINE) {
1278             lv_draw_rect(&cur_area, mask, &cur_style, opa_scale);
1279         } else if(ext->cursor.type == LV_CURSOR_BLOCK) {
1280             lv_draw_rect(&cur_area, mask, &cur_style, opa_scale);
1281 
1282             char letter_buf[8] = {0};
1283             memcpy(letter_buf, &txt[ext->cursor.txt_byte_pos], lv_txt_encoded_size(&txt[ext->cursor.txt_byte_pos]));
1284 
1285             cur_area.x1 += cur_style.body.padding.left;
1286             cur_area.y1 += cur_style.body.padding.top;
1287             lv_draw_label(&cur_area, mask, &cur_style, opa_scale, letter_buf, LV_TXT_FLAG_NONE, 0,
1288                           LV_LABEL_TEXT_SEL_OFF, LV_LABEL_TEXT_SEL_OFF, NULL);
1289 
1290         } else if(ext->cursor.type == LV_CURSOR_OUTLINE) {
1291             cur_style.body.opa = LV_OPA_TRANSP;
1292             if(cur_style.body.border.width == 0) cur_style.body.border.width = 1; /*Be sure the border will be drawn*/
1293             lv_draw_rect(&cur_area, mask, &cur_style, opa_scale);
1294         } else if(ext->cursor.type == LV_CURSOR_UNDERLINE) {
1295             lv_draw_rect(&cur_area, mask, &cur_style, opa_scale);
1296         }
1297     }
1298 
1299     return true;
1300 }
1301 
1302 /**
1303  * Signal function of the text area
1304  * @param ta pointer to a text area object
1305  * @param sign a signal type from lv_signal_t enum
1306  * @param param pointer to a signal specific variable
1307  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
1308  */
lv_ta_signal(lv_obj_t * ta,lv_signal_t sign,void * param)1309 static lv_res_t lv_ta_signal(lv_obj_t * ta, lv_signal_t sign, void * param)
1310 {
1311     lv_res_t res;
1312 
1313     /* Include the ancient signal function */
1314     res = ancestor_signal(ta, sign, param);
1315     if(res != LV_RES_OK) return res;
1316 
1317     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1318     if(sign == LV_SIGNAL_CLEANUP) {
1319         if(ext->pwd_tmp != NULL) lv_mem_free(ext->pwd_tmp);
1320 
1321         /* (The created label will be deleted automatically) */
1322     } else if(sign == LV_SIGNAL_STYLE_CHG) {
1323         if(ext->label) {
1324             lv_obj_t * scrl               = lv_page_get_scrl(ta);
1325             const lv_style_t * style_ta   = lv_obj_get_style(ta);
1326             const lv_style_t * style_scrl = lv_obj_get_style(scrl);
1327             if(ext->one_line) {
1328                 /*In one line mode refresh the Text Area height because 'vpad' can modify it*/
1329                 const lv_style_t * style_label = lv_obj_get_style(ext->label);
1330                 lv_coord_t font_h              = lv_font_get_line_height(style_label->text.font);
1331                 lv_obj_set_height(ta, font_h + style_ta->body.padding.top + style_ta->body.padding.bottom +
1332                                           style_scrl->body.padding.top + style_scrl->body.padding.bottom);
1333             } else {
1334                 /*In not one line mode refresh the Label width because 'hpad' can modify it*/
1335                 lv_obj_set_width(ext->label, lv_page_get_fit_width(ta));
1336                 lv_obj_set_pos(ext->label, style_scrl->body.padding.left,
1337                                style_scrl->body.padding.right); /*Be sure the Label is in the correct position*/
1338 
1339                 if(ext->placeholder) {
1340                     lv_obj_set_width(ext->placeholder, lv_page_get_fit_width(ta));
1341                     lv_obj_set_pos(ext->placeholder, style_scrl->body.padding.left,
1342                                    style_scrl->body.padding.top); /*Be sure the placeholder is in the correct position*/
1343                 }
1344             }
1345             lv_label_set_text(ext->label, NULL);
1346         }
1347     } else if(sign == LV_SIGNAL_CORD_CHG) {
1348         /*Set the label width according to the text area width*/
1349         if(ext->label) {
1350             if(lv_obj_get_width(ta) != lv_area_get_width(param) || lv_obj_get_height(ta) != lv_area_get_height(param)) {
1351                 lv_obj_t * scrl               = lv_page_get_scrl(ta);
1352                 const lv_style_t * style_scrl = lv_obj_get_style(scrl);
1353                 lv_obj_set_width(ext->label, lv_page_get_fit_width(ta));
1354                 lv_obj_set_pos(ext->label, style_scrl->body.padding.left, style_scrl->body.padding.top);
1355                 lv_label_set_text(ext->label, NULL); /*Refresh the label*/
1356 
1357                 refr_cursor_area(ta);
1358             }
1359         }
1360         /*Set the placeholder width according to the text area width*/
1361         if(ext->placeholder) {
1362             if(lv_obj_get_width(ta) != lv_area_get_width(param) || lv_obj_get_height(ta) != lv_area_get_height(param)) {
1363                 lv_obj_t * scrl               = lv_page_get_scrl(ta);
1364                 const lv_style_t * style_scrl = lv_obj_get_style(scrl);
1365                 lv_obj_set_width(ext->placeholder, lv_page_get_fit_width(ta));
1366                 lv_obj_set_pos(ext->placeholder, style_scrl->body.padding.left, style_scrl->body.padding.top);
1367                 lv_label_set_text(ext->placeholder, NULL); /*Refresh the label*/
1368 
1369                 refr_cursor_area(ta);
1370             }
1371         }
1372     } else if(sign == LV_SIGNAL_CONTROL) {
1373         uint32_t c = *((uint32_t *)param); /*uint32_t because can be UTF-8*/
1374         if(c == LV_KEY_RIGHT)
1375             lv_ta_cursor_right(ta);
1376         else if(c == LV_KEY_LEFT)
1377             lv_ta_cursor_left(ta);
1378         else if(c == LV_KEY_UP)
1379             lv_ta_cursor_up(ta);
1380         else if(c == LV_KEY_DOWN)
1381             lv_ta_cursor_down(ta);
1382         else if(c == LV_KEY_BACKSPACE)
1383             lv_ta_del_char(ta);
1384         else if(c == LV_KEY_DEL)
1385             lv_ta_del_char_forward(ta);
1386         else if(c == LV_KEY_HOME)
1387             lv_ta_set_cursor_pos(ta, 0);
1388         else if(c == LV_KEY_END)
1389             lv_ta_set_cursor_pos(ta, LV_TA_CURSOR_LAST);
1390         else {
1391             lv_ta_add_char(ta, c);
1392         }
1393     } else if(sign == LV_SIGNAL_GET_EDITABLE) {
1394         bool * editable = (bool *)param;
1395         *editable       = true;
1396     } else if(sign == LV_SIGNAL_GET_TYPE) {
1397         lv_obj_type_t * buf = param;
1398         uint8_t i;
1399         for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
1400             if(buf->type[i] == NULL) break;
1401         }
1402         buf->type[i] = "lv_ta";
1403     } else if(sign == LV_SIGNAL_DEFOCUS) {
1404         lv_cursor_type_t cur_type;
1405         cur_type = lv_ta_get_cursor_type(ta);
1406         lv_ta_set_cursor_type(ta, cur_type | LV_CURSOR_HIDDEN);
1407     } else if(sign == LV_SIGNAL_FOCUS) {
1408 #if LV_USE_GROUP
1409         lv_cursor_type_t cur_type;
1410         cur_type                   = lv_ta_get_cursor_type(ta);
1411         lv_group_t * g             = lv_obj_get_group(ta);
1412         bool editing               = lv_group_get_editing(g);
1413         lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
1414 
1415         /*Encoders need special handling*/
1416         if(indev_type == LV_INDEV_TYPE_ENCODER) {
1417             if(editing)
1418                 lv_ta_set_cursor_type(ta, cur_type & (~LV_CURSOR_HIDDEN));
1419             else
1420                 lv_ta_set_cursor_type(ta, cur_type | LV_CURSOR_HIDDEN);
1421         } else {
1422             lv_ta_set_cursor_type(ta, cur_type & (~LV_CURSOR_HIDDEN));
1423         }
1424 #endif
1425     } else if(sign == LV_SIGNAL_PRESSED || sign == LV_SIGNAL_PRESSING || sign == LV_SIGNAL_PRESS_LOST ||
1426               sign == LV_SIGNAL_RELEASED) {
1427         update_cursor_position_on_click(ta, sign, (lv_indev_t *)param);
1428     }
1429     return res;
1430 }
1431 
1432 /**
1433  * Signal function of the scrollable part of the text area
1434  * @param scrl pointer to scrollable part of a text area object
1435  * @param sign a signal type from lv_signal_t enum
1436  * @param param pointer to a signal specific variable
1437  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
1438  */
lv_ta_scrollable_signal(lv_obj_t * scrl,lv_signal_t sign,void * param)1439 static lv_res_t lv_ta_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, void * param)
1440 {
1441     lv_res_t res;
1442 
1443     /* Include the ancient signal function */
1444     res = scrl_signal(scrl, sign, param);
1445     if(res != LV_RES_OK) return res;
1446 
1447     lv_obj_t * ta     = lv_obj_get_parent(scrl);
1448     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1449 
1450     if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) {
1451         /*Set ext. size because the cursor might be out of this object*/
1452         const lv_style_t * style_label = lv_obj_get_style(ext->label);
1453         lv_coord_t font_h              = lv_font_get_line_height(style_label->text.font);
1454         scrl->ext_draw_pad             = LV_MATH_MAX(scrl->ext_draw_pad, style_label->text.line_space + font_h);
1455     } else if(sign == LV_SIGNAL_CORD_CHG) {
1456         /*Set the label width according to the text area width*/
1457         if(ext->label) {
1458             if(lv_obj_get_width(scrl) != lv_area_get_width(param) ||
1459                lv_obj_get_height(scrl) != lv_area_get_height(param)) {
1460 
1461                 const lv_style_t * style_scrl = lv_obj_get_style(scrl);
1462                 lv_obj_set_width(ext->label, lv_page_get_fit_width(ta));
1463                 lv_obj_set_pos(ext->label, style_scrl->body.padding.left, style_scrl->body.padding.top);
1464                 lv_label_set_text(ext->label, NULL); /*Refresh the label*/
1465 
1466                 refr_cursor_area(ta);
1467             }
1468         }
1469     } else if(sign == LV_SIGNAL_PRESSING || sign == LV_SIGNAL_PRESSED || sign == LV_SIGNAL_PRESS_LOST ||
1470               sign == LV_SIGNAL_RELEASED) {
1471         update_cursor_position_on_click(ta, sign, (lv_indev_t *)param);
1472     }
1473 
1474     return res;
1475 }
1476 
1477 #if LV_USE_ANIMATION
1478 
1479 /**
1480  * Called to blink the cursor
1481  * @param ta pointer to a text area
1482  * @param hide 1: hide the cursor, 0: show it
1483  */
cursor_blink_anim(lv_obj_t * ta,lv_anim_value_t show)1484 static void cursor_blink_anim(lv_obj_t * ta, lv_anim_value_t show)
1485 {
1486     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1487     if(show != ext->cursor.state) {
1488         ext->cursor.state = show == 0 ? 0 : 1;
1489         if(ext->cursor.type != LV_CURSOR_NONE && (ext->cursor.type & LV_CURSOR_HIDDEN) == 0) {
1490             lv_disp_t * disp = lv_obj_get_disp(ta);
1491             lv_area_t area_tmp;
1492             lv_area_copy(&area_tmp, &ext->cursor.area);
1493             area_tmp.x1 += ext->label->coords.x1;
1494             area_tmp.y1 += ext->label->coords.y1;
1495             area_tmp.x2 += ext->label->coords.x1;
1496             area_tmp.y2 += ext->label->coords.y1;
1497             lv_inv_area(disp, &area_tmp);
1498         }
1499     }
1500 }
1501 
1502 /**
1503  * Dummy function to animate char hiding in pwd mode.
1504  * Does nothing, but a function is required in car hiding anim.
1505  * (pwd_char_hider callback do the real job)
1506  * @param ta unused
1507  * @param x unused
1508  */
pwd_char_hider_anim(lv_obj_t * ta,lv_anim_value_t x)1509 static void pwd_char_hider_anim(lv_obj_t * ta, lv_anim_value_t x)
1510 {
1511     (void)ta;
1512     (void)x;
1513 }
1514 
1515 /**
1516  * Call when an animation is ready to convert all characters to '*'
1517  * @param a pointer to the animation
1518  */
pwd_char_hider_anim_ready(lv_anim_t * a)1519 static void pwd_char_hider_anim_ready(lv_anim_t * a)
1520 {
1521     lv_obj_t * ta = a->var;
1522     pwd_char_hider(ta);
1523 }
1524 #endif
1525 
1526 /**
1527  * Hide all characters (convert them to '*')
1528  * @param ta: pointer to text area object
1529  */
pwd_char_hider(lv_obj_t * ta)1530 static void pwd_char_hider(lv_obj_t * ta)
1531 {
1532     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1533     if(ext->pwd_mode != 0) {
1534         char * txt  = lv_label_get_text(ext->label);
1535         int16_t len = lv_txt_get_encoded_length(txt);
1536         bool refr   = false;
1537         uint16_t i;
1538         for(i = 0; i < len; i++) {
1539             txt[i] = '*';
1540             refr   = true;
1541         }
1542 
1543         txt[i] = '\0';
1544 
1545         if(refr != false) lv_label_set_text(ext->label, txt);
1546     }
1547 }
1548 
1549 /**
1550  * Test an unicode character if it is accepted or not. Checks max length and accepted char list.
1551  * @param ta pointer to a test area object
1552  * @param c an unicode character
1553  * @return true: accapted; false: rejected
1554  */
char_is_accepted(lv_obj_t * ta,uint32_t c)1555 static bool char_is_accepted(lv_obj_t * ta, uint32_t c)
1556 {
1557     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1558 
1559     /*If no restriction accept it*/
1560     if(ext->accapted_chars == NULL && ext->max_length == 0) return true;
1561 
1562     /*Too many characters?*/
1563     if(ext->max_length > 0 && lv_txt_get_encoded_length(lv_ta_get_text(ta)) >= ext->max_length) {
1564         return false;
1565     }
1566 
1567     /*Accepted character?*/
1568     if(ext->accapted_chars) {
1569         uint32_t i = 0;
1570         uint32_t a;
1571         while(ext->accapted_chars[i] != '\0') {
1572             a = lv_txt_encoded_next(ext->accapted_chars, &i);
1573             if(a == c) return true; /*Accepted*/
1574         }
1575 
1576         return false; /*The character wasn't in the list*/
1577     } else {
1578         return true; /*If the accepted char list in not specified the accept the character*/
1579     }
1580 }
1581 
get_cursor_style(lv_obj_t * ta,lv_style_t * style_res)1582 static void get_cursor_style(lv_obj_t * ta, lv_style_t * style_res)
1583 {
1584     lv_ta_ext_t * ext              = lv_obj_get_ext_attr(ta);
1585     const lv_style_t * label_style = lv_obj_get_style(ext->label);
1586 
1587     if(ext->cursor.style) {
1588         lv_style_copy(style_res, ext->cursor.style);
1589     } else {
1590         /*If cursor style is not specified then use the modified label style */
1591         lv_style_copy(style_res, label_style);
1592         lv_color_t clv_color_tmp = style_res->text.color; /*Make letter color to cursor color*/
1593         style_res->text.color =
1594             style_res->body.main_color; /*In block mode the letter color will be current background color*/
1595         style_res->body.main_color     = clv_color_tmp;
1596         style_res->body.grad_color     = clv_color_tmp;
1597         style_res->body.border.color   = clv_color_tmp;
1598         style_res->body.border.opa     = LV_OPA_COVER;
1599         style_res->body.border.width   = 1;
1600         style_res->body.shadow.width   = 0;
1601         style_res->body.radius         = 0;
1602         style_res->body.opa            = LV_OPA_COVER;
1603         style_res->body.padding.left   = 0;
1604         style_res->body.padding.right  = 0;
1605         style_res->body.padding.top    = 0;
1606         style_res->body.padding.bottom = 0;
1607         style_res->line.width          = 1;
1608         style_res->body.opa            = LV_OPA_COVER;
1609     }
1610 }
1611 
refr_cursor_area(lv_obj_t * ta)1612 static void refr_cursor_area(lv_obj_t * ta)
1613 {
1614     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1615 
1616     const lv_style_t * label_style = lv_obj_get_style(ext->label);
1617 
1618     lv_style_t cur_style;
1619     get_cursor_style(ta, &cur_style);
1620 
1621     uint16_t cur_pos = lv_ta_get_cursor_pos(ta);
1622     const char * txt = lv_label_get_text(ext->label);
1623 
1624     uint32_t byte_pos;
1625     byte_pos = lv_txt_encoded_get_byte_id(txt, cur_pos);
1626 
1627     uint32_t letter = lv_txt_encoded_next(&txt[byte_pos], NULL);
1628 
1629     lv_coord_t letter_h = lv_font_get_line_height(label_style->text.font);
1630 
1631     /*Set letter_w (set not 0 on non printable but valid chars)*/
1632     lv_coord_t letter_w;
1633     if(letter == '\0' || letter == '\n' || letter == '\r') {
1634         letter_w = lv_font_get_glyph_width(label_style->text.font, ' ', '\0');
1635     } else {
1636         /*`letter_next` parameter is '\0' to ignore kerning*/
1637         letter_w = lv_font_get_glyph_width(label_style->text.font, letter, '\0');
1638     }
1639 
1640     lv_point_t letter_pos;
1641     lv_label_get_letter_pos(ext->label, cur_pos, &letter_pos);
1642 
1643     /*If the cursor is out of the text (most right) draw it to the next line*/
1644     if(letter_pos.x + ext->label->coords.x1 + letter_w > ext->label->coords.x2 && ext->one_line == 0 &&
1645        lv_label_get_align(ext->label) != LV_LABEL_ALIGN_RIGHT) {
1646         letter_pos.x = 0;
1647         letter_pos.y += letter_h + label_style->text.line_space;
1648 
1649         if(letter != '\0') {
1650             byte_pos += lv_txt_encoded_size(&txt[byte_pos]);
1651             letter = lv_txt_encoded_next(&txt[byte_pos], NULL);
1652         }
1653 
1654         if(letter == '\0' || letter == '\n' || letter == '\r') {
1655             letter_w = lv_font_get_glyph_width(label_style->text.font, ' ', '\0');
1656         } else {
1657             letter_w = lv_font_get_glyph_width(label_style->text.font, letter, '\0');
1658         }
1659     }
1660 
1661     /*Save the byte position. It is required to draw `LV_CURSOR_BLOCK`*/
1662     ext->cursor.txt_byte_pos = byte_pos;
1663 
1664     /*Draw he cursor according to the type*/
1665     lv_area_t cur_area;
1666 
1667     if(ext->cursor.type == LV_CURSOR_LINE) {
1668         cur_area.x1 =
1669             letter_pos.x + cur_style.body.padding.left - (cur_style.line.width >> 1) - (cur_style.line.width & 0x1);
1670         cur_area.y1 = letter_pos.y + cur_style.body.padding.top;
1671         cur_area.x2 = letter_pos.x + cur_style.body.padding.right + (cur_style.line.width >> 1);
1672         cur_area.y2 = letter_pos.y + cur_style.body.padding.bottom + letter_h;
1673     } else if(ext->cursor.type == LV_CURSOR_BLOCK) {
1674         cur_area.x1 = letter_pos.x - cur_style.body.padding.left;
1675         cur_area.y1 = letter_pos.y - cur_style.body.padding.top;
1676         cur_area.x2 = letter_pos.x + cur_style.body.padding.right + letter_w;
1677         cur_area.y2 = letter_pos.y + cur_style.body.padding.bottom + letter_h;
1678 
1679     } else if(ext->cursor.type == LV_CURSOR_OUTLINE) {
1680         cur_area.x1 = letter_pos.x - cur_style.body.padding.left;
1681         cur_area.y1 = letter_pos.y - cur_style.body.padding.top;
1682         cur_area.x2 = letter_pos.x + cur_style.body.padding.right + letter_w;
1683         cur_area.y2 = letter_pos.y + cur_style.body.padding.bottom + letter_h;
1684     } else if(ext->cursor.type == LV_CURSOR_UNDERLINE) {
1685         cur_area.x1 = letter_pos.x + cur_style.body.padding.left;
1686         cur_area.y1 = letter_pos.y + cur_style.body.padding.top + letter_h - (cur_style.line.width >> 1);
1687         cur_area.x2 = letter_pos.x + cur_style.body.padding.right + letter_w;
1688         cur_area.y2 = letter_pos.y + cur_style.body.padding.bottom + letter_h + (cur_style.line.width >> 1) +
1689                       (cur_style.line.width & 0x1);
1690     }
1691 
1692     /*Save the new area*/
1693     lv_disp_t * disp = lv_obj_get_disp(ta);
1694     lv_area_t area_tmp;
1695     lv_area_copy(&area_tmp, &ext->cursor.area);
1696     area_tmp.x1 += ext->label->coords.x1;
1697     area_tmp.y1 += ext->label->coords.y1;
1698     area_tmp.x2 += ext->label->coords.x1;
1699     area_tmp.y2 += ext->label->coords.y1;
1700     lv_inv_area(disp, &area_tmp);
1701 
1702     lv_area_copy(&ext->cursor.area, &cur_area);
1703 
1704     lv_area_copy(&area_tmp, &ext->cursor.area);
1705     area_tmp.x1 += ext->label->coords.x1;
1706     area_tmp.y1 += ext->label->coords.y1;
1707     area_tmp.x2 += ext->label->coords.x1;
1708     area_tmp.y2 += ext->label->coords.y1;
1709     lv_inv_area(disp, &area_tmp);
1710 }
1711 
placeholder_update(lv_obj_t * ta)1712 static void placeholder_update(lv_obj_t * ta)
1713 {
1714     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1715     const char * ta_text;
1716 
1717     if(ext->placeholder == NULL) return;
1718 
1719     ta_text = lv_ta_get_text(ta);
1720 
1721     if(ta_text[0] == '\0') {
1722         /*Be sure the main label and the placeholder has the same coordinates*/
1723         lv_obj_t * scrl               = lv_page_get_scrl(ta);
1724         const lv_style_t * style_scrl = lv_obj_get_style(scrl);
1725         lv_obj_set_pos(ext->placeholder, style_scrl->body.padding.left, style_scrl->body.padding.top);
1726         lv_obj_set_pos(ext->label, style_scrl->body.padding.left, style_scrl->body.padding.top);
1727 
1728         lv_obj_set_width(ext->placeholder, lv_page_get_fit_width(ta));
1729         lv_obj_set_hidden(ext->placeholder, false);
1730     } else {
1731         lv_obj_set_hidden(ext->placeholder, true);
1732     }
1733 }
1734 
update_cursor_position_on_click(lv_obj_t * ta,lv_signal_t sign,lv_indev_t * click_source)1735 static void update_cursor_position_on_click(lv_obj_t * ta, lv_signal_t sign, lv_indev_t * click_source)
1736 {
1737 
1738     if(click_source == NULL) return;
1739 
1740     lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta);
1741     if(ext->cursor.click_pos == 0) return;
1742     if(ext->cursor.type == LV_CURSOR_NONE) return;
1743 
1744     if(lv_indev_get_type(click_source) == LV_INDEV_TYPE_KEYPAD ||
1745        lv_indev_get_type(click_source) == LV_INDEV_TYPE_ENCODER) {
1746         return;
1747     }
1748 
1749     lv_area_t label_coords;
1750     lv_obj_get_coords(ext->label, &label_coords);
1751 
1752     lv_point_t point_act, vect_act;
1753     lv_indev_get_point(click_source, &point_act);
1754     lv_indev_get_vect(click_source, &vect_act);
1755 
1756     if(point_act.x < 0 || point_act.y < 0) return; /*Ignore event from keypad*/
1757     lv_point_t relative_position;
1758     relative_position.x = point_act.x - label_coords.x1;
1759     relative_position.y = point_act.y - label_coords.y1;
1760 
1761     lv_coord_t label_width = lv_obj_get_width(ext->label);
1762 
1763     uint16_t index_of_char_at_position;
1764 
1765 #if LV_LABEL_TEXT_SEL
1766     lv_label_ext_t * ext_label = lv_obj_get_ext_attr(ext->label);
1767     bool click_outside_label;
1768     /*Check if the click happened on the left side of the area outside the label*/
1769     if(relative_position.x < 0) {
1770         index_of_char_at_position = 0;
1771         click_outside_label       = true;
1772     }
1773     /*Check if the click happened on the right side of the area outside the label*/
1774     else if(relative_position.x >= label_width) {
1775         index_of_char_at_position = LV_TA_CURSOR_LAST;
1776         click_outside_label       = true;
1777     } else {
1778         index_of_char_at_position = lv_label_get_letter_on(ext->label, &relative_position);
1779         click_outside_label       = !lv_label_is_char_under_pos(ext->label, &relative_position);
1780     }
1781 
1782     if(ext->text_sel_en) {
1783         if(!ext->text_sel_in_prog && !click_outside_label && sign == LV_SIGNAL_PRESSED) {
1784             /*Input device just went down. Store the selection start position*/
1785             ext->tmp_sel_start    = index_of_char_at_position;
1786             ext->tmp_sel_end      = LV_LABEL_TEXT_SEL_OFF;
1787             ext->text_sel_in_prog = 1;
1788             lv_obj_set_drag(lv_page_get_scrl(ta), false);
1789         } else if(ext->text_sel_in_prog && sign == LV_SIGNAL_PRESSING) {
1790             /*Input device may be moving. Store the end position */
1791             ext->tmp_sel_end = index_of_char_at_position;
1792         } else if(ext->text_sel_in_prog && (sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_RELEASED)) {
1793             /*Input device is released. Check if anything was selected.*/
1794             lv_obj_set_drag(lv_page_get_scrl(ta), true);
1795         }
1796     }
1797 
1798     if(ext->text_sel_in_prog || sign == LV_SIGNAL_PRESSED) lv_ta_set_cursor_pos(ta, index_of_char_at_position);
1799 
1800     if(ext->text_sel_in_prog) {
1801         /*If the selected area has changed then update the real values and*/
1802         /*invalidate the text area.*/
1803 
1804         if(ext->tmp_sel_start > ext->tmp_sel_end) {
1805             if(ext_label->txt_sel_start != ext->tmp_sel_end || ext_label->txt_sel_end != ext->tmp_sel_start) {
1806                 ext_label->txt_sel_start = ext->tmp_sel_end;
1807                 ext_label->txt_sel_end   = ext->tmp_sel_start;
1808                 lv_obj_invalidate(ta);
1809             }
1810         } else if(ext->tmp_sel_start < ext->tmp_sel_end) {
1811             if(ext_label->txt_sel_start != ext->tmp_sel_start || ext_label->txt_sel_end != ext->tmp_sel_end) {
1812                 ext_label->txt_sel_start = ext->tmp_sel_start;
1813                 ext_label->txt_sel_end   = ext->tmp_sel_end;
1814                 lv_obj_invalidate(ta);
1815             }
1816         } else {
1817             if(ext_label->txt_sel_start != LV_LABEL_TEXT_SEL_OFF || ext_label->txt_sel_end != LV_LABEL_TEXT_SEL_OFF) {
1818                 ext_label->txt_sel_start = LV_LABEL_TEXT_SEL_OFF;
1819                 ext_label->txt_sel_end   = LV_LABEL_TEXT_SEL_OFF;
1820                 lv_obj_invalidate(ta);
1821             }
1822         }
1823         /*Finish selection if necessary */
1824         if(sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_RELEASED) {
1825             ext->text_sel_in_prog = 0;
1826         }
1827     }
1828 #else
1829     /*Check if the click happened on the left side of the area outside the label*/
1830     if(relative_position.x < 0) {
1831         index_of_char_at_position = 0;
1832     }
1833     /*Check if the click happened on the right side of the area outside the label*/
1834     else if(relative_position.x >= label_width) {
1835         index_of_char_at_position = LV_TA_CURSOR_LAST;
1836     } else {
1837         index_of_char_at_position = lv_label_get_letter_on(ext->label, &relative_position);
1838     }
1839 
1840     if(sign == LV_SIGNAL_PRESSED) lv_ta_set_cursor_pos(ta, index_of_char_at_position);
1841 #endif
1842 }
1843 
1844 #endif
1845