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