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