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