diff --git a/src/lv_core/lv_style.c b/src/lv_core/lv_style.c index 57c251d39..578403a90 100644 --- a/src/lv_core/lv_style.c +++ b/src/lv_core/lv_style.c @@ -95,6 +95,7 @@ void lv_style_init(void) lv_style_scr.text.opa = LV_OPA_COVER; lv_style_scr.text.color = lv_color_make(0x30, 0x30, 0x30); + lv_style_scr.text.sel_color = lv_color_make(0x55, 0x96, 0xd8); lv_style_scr.text.font = LV_FONT_DEFAULT; lv_style_scr.text.letter_space = 2; lv_style_scr.text.line_space = 2; diff --git a/src/lv_core/lv_style.h b/src/lv_core/lv_style.h index 3cde6854d..c27db5abc 100644 --- a/src/lv_core/lv_style.h +++ b/src/lv_core/lv_style.h @@ -84,6 +84,7 @@ typedef struct struct { lv_color_t color; + lv_color_t sel_color; const lv_font_t * font; lv_coord_t letter_space; lv_coord_t line_space; diff --git a/src/lv_draw/lv_draw_img.c b/src/lv_draw/lv_draw_img.c index b41c97a97..20bb667fa 100644 --- a/src/lv_draw/lv_draw_img.c +++ b/src/lv_draw/lv_draw_img.c @@ -71,7 +71,7 @@ void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, if(src == NULL) { LV_LOG_WARN("Image draw: src is NULL"); lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER); - lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL); + lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL, -1, -1); return; } @@ -81,7 +81,7 @@ void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, if(res == LV_RES_INV) { LV_LOG_WARN("Image draw error"); lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER); - lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL); + lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL, -1, -1); return; } } diff --git a/src/lv_draw/lv_draw_label.c b/src/lv_draw/lv_draw_label.c index a1febd9d9..e8baee80c 100644 --- a/src/lv_draw/lv_draw_label.c +++ b/src/lv_draw/lv_draw_label.c @@ -51,10 +51,11 @@ static uint8_t hex_char_to_num(char hex); * @param txt 0 terminated text to write * @param flag settings for the text from 'txt_flag_t' enum * @param offset text offset in x and y direction (NULL if unused) - * + * @param sel_start start index of selected area (-1 if none) + * @param sel_end end index of selected area (-1 if none) */ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale, - const char * txt, lv_txt_flag_t flag, lv_point_t * offset) + const char * txt, lv_txt_flag_t flag, lv_point_t * offset, int sel_start, int sel_end) { const lv_font_t * font = style->text.font; lv_coord_t w; @@ -121,6 +122,9 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_st uint16_t par_start = 0; lv_color_t recolor; lv_coord_t letter_w; + lv_style_t sel_style; + lv_style_copy(&sel_style, &lv_style_plain_color); + sel_style.body.main_color = sel_style.body.grad_color = style->text.sel_color; /*Write out all lines*/ while(txt[line_start] != '\0') { @@ -175,9 +179,23 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_st if(cmd_state == CMD_STATE_IN) color = recolor; - lv_draw_letter(&pos, mask, font, letter, color, opa); letter_w = lv_font_get_width(font, letter); + if(sel_start != -1 && sel_end != -1) { + int char_ind = lv_encoded_get_char_id(txt, i); + /*Do not draw the rectangle on the character at `sel_start`.*/ + if(char_ind > sel_start && char_ind <= sel_end) { + lv_area_t sel_coords; + sel_coords.x1 = pos.x; + sel_coords.y1 = pos.y; + sel_coords.x2 = pos.x + letter_w + style->text.letter_space - 1; + sel_coords.y2 = pos.y + line_height - 1; + lv_draw_rect(&sel_coords, mask, &sel_style, opa); + } + } + lv_draw_letter(&pos, mask, font, letter, color, opa); + + if(letter_w > 0){ pos.x += letter_w + style->text.letter_space; } diff --git a/src/lv_draw/lv_draw_label.h b/src/lv_draw/lv_draw_label.h index 8798573d3..96b3a88d2 100644 --- a/src/lv_draw/lv_draw_label.h +++ b/src/lv_draw/lv_draw_label.h @@ -36,10 +36,11 @@ extern "C" { * @param txt 0 terminated text to write * @param flag settings for the text from 'txt_flag_t' enum * @param offset text offset in x and y direction (NULL if unused) - * + * @param sel_start start index of selected area (-1 if none) + * @param sel_end end index of selected area (-1 if none) */ void lv_draw_label(const lv_area_t * coords,const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale, - const char * txt, lv_txt_flag_t flag, lv_point_t * offset); + const char * txt, lv_txt_flag_t flag, lv_point_t * offset, int sel_start, int sel_end); /********************** * MACROS diff --git a/src/lv_objx/lv_btnm.c b/src/lv_objx/lv_btnm.c index c5c5485b1..f0dd14c32 100644 --- a/src/lv_objx/lv_btnm.c +++ b/src/lv_objx/lv_btnm.c @@ -683,7 +683,7 @@ static bool lv_btnm_design(lv_obj_t * btnm, const lv_area_t * mask, lv_design_mo area_tmp.x2 = area_tmp.x1 + txt_size.x; area_tmp.y2 = area_tmp.y1 + txt_size.y; - lv_draw_label(&area_tmp, mask, btn_style, opa_scale, ext->map_p[txt_i], txt_flag, NULL); + lv_draw_label(&area_tmp, mask, btn_style, opa_scale, ext->map_p[txt_i], txt_flag, NULL, -1, -1); } } return true; diff --git a/src/lv_objx/lv_calendar.c b/src/lv_objx/lv_calendar.c index a92e2dfc3..cc8087de5 100644 --- a/src/lv_objx/lv_calendar.c +++ b/src/lv_objx/lv_calendar.c @@ -686,19 +686,19 @@ static void draw_header(lv_obj_t * calendar, const lv_area_t * mask) txt_buf[5] = '\0'; strcpy(&txt_buf[5], get_month_name(calendar, ext->showed_date.month)); header_area.y1 += ext->style_header->body.padding.top; - lv_draw_label(&header_area, mask, ext->style_header, opa_scale, txt_buf, LV_TXT_FLAG_CENTER, NULL); + lv_draw_label(&header_area, mask, ext->style_header, opa_scale, txt_buf, LV_TXT_FLAG_CENTER, NULL, -1, -1); /*Add the left arrow*/ lv_style_t * arrow_style = ext->btn_pressing < 0 ? ext->style_header_pr : ext->style_header; header_area.x1 += ext->style_header->body.padding.left; - lv_draw_label(&header_area, mask, arrow_style, opa_scale, LV_SYMBOL_LEFT, LV_TXT_FLAG_NONE, NULL); + lv_draw_label(&header_area, mask, arrow_style, opa_scale, LV_SYMBOL_LEFT, LV_TXT_FLAG_NONE, NULL, -1, -1); /*Add the right arrow*/ arrow_style = ext->btn_pressing > 0 ? ext->style_header_pr : ext->style_header; header_area.x1 = header_area.x2 - ext->style_header->body.padding.right - lv_txt_get_width(LV_SYMBOL_RIGHT, strlen(LV_SYMBOL_RIGHT), arrow_style->text.font, arrow_style->text.line_space, LV_TXT_FLAG_NONE); - lv_draw_label(&header_area, mask, arrow_style, opa_scale, LV_SYMBOL_RIGHT, LV_TXT_FLAG_NONE, NULL); + lv_draw_label(&header_area, mask, arrow_style, opa_scale, LV_SYMBOL_RIGHT, LV_TXT_FLAG_NONE, NULL, -1, -1); } @@ -722,7 +722,7 @@ static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask) for(i = 0; i < 7; i++) { label_area.x1 = calendar->coords.x1 + (w * i) / 7 + l_pad; label_area.x2 = label_area.x1 + box_w; - lv_draw_label(&label_area, mask, ext->style_day_names, opa_scale, get_day_name(calendar, i), LV_TXT_FLAG_CENTER, NULL); + lv_draw_label(&label_area, mask, ext->style_day_names, opa_scale, get_day_name(calendar, i), LV_TXT_FLAG_CENTER, NULL, -1, -1); } } @@ -852,7 +852,7 @@ static void draw_days(lv_obj_t * calendar, const lv_area_t * mask) /*Write the day's number*/ lv_utils_num_to_str(day_cnt, buf); - lv_draw_label(&label_area, mask, final_style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL); + lv_draw_label(&label_area, mask, final_style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL, -1, -1); /*Go to the next day*/ day_cnt ++; diff --git a/src/lv_objx/lv_chart.c b/src/lv_objx/lv_chart.c index 92f545d53..5dcd3be20 100644 --- a/src/lv_objx/lv_chart.c +++ b/src/lv_objx/lv_chart.c @@ -1069,7 +1069,7 @@ static void lv_chart_draw_y_ticks(lv_obj_t * chart, const lv_area_t * mask) /* set the area at some distance of the major tick len left of the tick */ lv_area_t a = {(p2.x - size.x - LV_CHART_AXIS_TO_LABEL_DISTANCE) , (p2.y - size.y/2), (p2.x - LV_CHART_AXIS_TO_LABEL_DISTANCE), (p2.y + size.y/2) }; - lv_draw_label(&a, mask, style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL); + lv_draw_label(&a, mask, style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL, -1, -1); } } } @@ -1183,7 +1183,7 @@ static void lv_chart_draw_x_ticks(lv_obj_t * chart, const lv_area_t * mask) /* set the area at some distance of the major tick len under of the tick */ lv_area_t a = { (p2.x - size.x/2) , (p2.y + LV_CHART_AXIS_TO_LABEL_DISTANCE), (p2.x + size.x/2), (p2.y + size.y + LV_CHART_AXIS_TO_LABEL_DISTANCE) }; - lv_draw_label(&a, mask, style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL); + lv_draw_label(&a, mask, style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL, -1, -1); } } } diff --git a/src/lv_objx/lv_ddlist.c b/src/lv_objx/lv_ddlist.c index ed4ee8e7c..e98d73426 100644 --- a/src/lv_objx/lv_ddlist.c +++ b/src/lv_objx/lv_ddlist.c @@ -574,7 +574,7 @@ static bool lv_ddlist_design(lv_obj_t * ddlist, const lv_area_t * mask, lv_desig new_style.text.opa = sel_style->text.opa; lv_txt_flag_t flag = lv_ddlist_get_txt_flag(ddlist); lv_draw_label(&ext->label->coords, &mask_sel, &new_style, opa_scale, - lv_label_get_text(ext->label), flag, NULL); + lv_label_get_text(ext->label), flag, NULL, -1, -1); } } @@ -606,7 +606,7 @@ static bool lv_ddlist_design(lv_obj_t * ddlist, const lv_area_t * mask, lv_desig if (area_ok) { lv_draw_label(&area_arrow, &mask_arrow, &new_style, opa_scale, - LV_SYMBOL_DOWN, LV_TXT_FLAG_NONE, NULL); /*Use a down arrow in ddlist, you can replace it with your custom symbol*/ + LV_SYMBOL_DOWN, LV_TXT_FLAG_NONE, NULL, -1, -1); /*Use a down arrow in ddlist, you can replace it with your custom symbol*/ } } } diff --git a/src/lv_objx/lv_gauge.c b/src/lv_objx/lv_gauge.c index 385d9f9bc..3b443198e 100644 --- a/src/lv_objx/lv_gauge.c +++ b/src/lv_objx/lv_gauge.c @@ -385,7 +385,7 @@ static void lv_gauge_draw_scale(lv_obj_t * gauge, const lv_area_t * mask) label_cord.x2 = label_cord.x1 + label_size.x; label_cord.y2 = label_cord.y1 + label_size.y; - lv_draw_label(&label_cord, mask, style, opa_scale, scale_txt, LV_TXT_FLAG_NONE, NULL); + lv_draw_label(&label_cord, mask, style, opa_scale, scale_txt, LV_TXT_FLAG_NONE, NULL, -1, -1); } } /** diff --git a/src/lv_objx/lv_img.c b/src/lv_objx/lv_img.c index 02ad804b3..214ac89cc 100644 --- a/src/lv_objx/lv_img.c +++ b/src/lv_objx/lv_img.c @@ -388,7 +388,7 @@ static bool lv_img_design(lv_obj_t * img, const lv_area_t * mask, lv_design_mode lv_style_t style_mod; lv_style_copy(&style_mod, style); style_mod.text.color = style->image.color; - lv_draw_label(&coords, mask, &style_mod, opa_scale, ext->src, LV_TXT_FLAG_NONE, NULL); + lv_draw_label(&coords, mask, &style_mod, opa_scale, ext->src, LV_TXT_FLAG_NONE, NULL, -1, -1); } else { /*Trigger the error handler of image drawer*/ LV_LOG_WARN("lv_img_design: image source type is unknown"); diff --git a/src/lv_objx/lv_label.c b/src/lv_objx/lv_label.c index 95c1f3742..2ab9aae27 100644 --- a/src/lv_objx/lv_label.c +++ b/src/lv_objx/lv_label.c @@ -90,6 +90,9 @@ lv_obj_t * lv_label_create(lv_obj_t * par, const lv_obj_t * copy) ext->anim_speed = LV_LABEL_SCROLL_SPEED; ext->offset.x = 0; ext->offset.y = 0; + ext->selection_start = -1; + ext->selection_end = -1; + lv_obj_set_design_cb(new_label, lv_label_design); lv_obj_set_signal_cb(new_label, lv_label_signal); @@ -543,6 +546,79 @@ uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos) return lv_encoded_get_char_id(txt, i); } +/** + * Check if a character is drawn under a point. + * @param label Label object + * @param pos Point to check for characte under + * @return whether a character is drawn under the point + */ +bool lv_label_is_char_under_pos(const lv_obj_t * label, lv_point_t * pos) { + const char * txt = lv_label_get_text(label); + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + uint32_t line_start = 0; + uint32_t new_line_start = 0; + lv_coord_t max_w = lv_obj_get_width(label); + lv_style_t * style = lv_obj_get_style(label); + const lv_font_t * font = style->text.font; + uint8_t letter_height = lv_font_get_height(font); + lv_coord_t y = 0; + lv_txt_flag_t flag = LV_TXT_FLAG_NONE; + + if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR; + if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND; + if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER; + + /*If the width will be expanded set the max length to very big */ + if(ext->long_mode == LV_LABEL_LONG_EXPAND || ext->long_mode == LV_LABEL_LONG_SCROLL) { + max_w = LV_COORD_MAX; + } + + /*Search the line of the index letter */; + while(txt[line_start] != '\0') { + new_line_start += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, max_w, flag); + + if(pos->y <= y + letter_height) break; /*The line is found (stored in 'line_start')*/ + y += letter_height + style->text.line_space; + + line_start = new_line_start; + } + + /*Calculate the x coordinate*/ + lv_coord_t x = 0; + lv_coord_t last_x = 0; + if(ext->align == LV_LABEL_ALIGN_CENTER) { + lv_coord_t line_w; + line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start, + font, style->text.letter_space, flag); + x += lv_obj_get_width(label) / 2 - line_w / 2; + } + + lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT; + uint32_t i = line_start; + uint32_t i_current = i; + uint32_t letter = 0; + while(i <= new_line_start - 1) { + letter = lv_txt_encoded_next(txt, &i); /*Be careful 'i' already points to the next character*/ + /*Handle the recolor command*/ + if((flag & LV_TXT_FLAG_RECOLOR) != 0) { + if(lv_txt_is_cmd(&cmd_state, txt[i]) != false) { + continue; /*Skip the letter is it is part of a command*/ + } + } + last_x = x; + x += lv_font_get_width(font, letter); + if(pos->x < x) { + i = i_current; + break; + } + x += style->text.letter_space; + i_current = i; + } + + int max_diff = lv_font_get_width(font, letter) + style->text.letter_space + 1; + return (pos->x >= (last_x - style->text.letter_space) && pos->x <= (last_x + max_diff)); +} + /*===================== * Other functions @@ -671,7 +747,7 @@ static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_ } } - lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ext->offset); + lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ext->offset, ext->selection_start, ext->selection_end); } return true; } diff --git a/src/lv_objx/lv_label.h b/src/lv_objx/lv_label.h index 73adae9db..d1f10ae55 100644 --- a/src/lv_objx/lv_label.h +++ b/src/lv_objx/lv_label.h @@ -73,6 +73,8 @@ typedef struct uint8_t recolor :1; /*Enable in-line letter re-coloring*/ uint8_t expand :1; /*Ignore real width (used by the library with LV_LABEL_LONG_ROLL)*/ uint8_t body_draw :1; /*Draw background body*/ + int selection_start; /*Left-most selection character*/ + int selection_end; /*Right-most selection character*/ } lv_label_ext_t; /********************** @@ -224,6 +226,14 @@ void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t */ uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos); +/** + * Check if a character is drawn under a point. + * @param label Label object + * @param pos Point to check for characte under + * @return whether a character is drawn under the point + */ +bool lv_label_is_char_under_pos(const lv_obj_t * label, lv_point_t * pos); + /** * Get the style of an label object * @param label pointer to an label object diff --git a/src/lv_objx/lv_roller.c b/src/lv_objx/lv_roller.c index 57057539c..61e9557ff 100644 --- a/src/lv_objx/lv_roller.c +++ b/src/lv_objx/lv_roller.c @@ -373,7 +373,7 @@ static bool lv_roller_design(lv_obj_t * roller, const lv_area_t * mask, lv_desig new_style.text.color = sel_style->text.color; new_style.text.opa = sel_style->text.opa; lv_draw_label(&ext->ddlist.label->coords, &mask_sel, &new_style, opa_scale, - lv_label_get_text(ext->ddlist.label), txt_align, NULL); + lv_label_get_text(ext->ddlist.label), txt_align, NULL, -1, -1); } } diff --git a/src/lv_objx/lv_ta.c b/src/lv_objx/lv_ta.c index 2ff7150b1..440f6ec6d 100644 --- a/src/lv_objx/lv_ta.c +++ b/src/lv_objx/lv_ta.c @@ -54,7 +54,7 @@ static bool char_is_accepted(lv_obj_t * ta, uint32_t c); static void get_cursor_style(lv_obj_t * ta, lv_style_t * style_res); static void refr_cursor_area(lv_obj_t * ta); static void placeholder_update(lv_obj_t * ta); -static void update_cursor_position_on_click(lv_obj_t * ta, lv_indev_t * click_source); +static void update_cursor_position_on_click(lv_obj_t * ta, lv_signal_t sign, lv_indev_t * click_source); /********************** * STATIC VARIABLES @@ -231,6 +231,7 @@ void lv_ta_add_char(lv_obj_t * ta, uint32_t c) if(ext->pwd_mode != 0) pwd_char_hider(ta); /*Make sure all the current text contains only '*'*/ lv_label_ins_text(ext->label, ext->cursor.pos, (const char *)letter_buf); /*Insert the character*/ + lv_ta_clear_selection(ta); /*Clear selection*/ if(ext->pwd_mode != 0) { @@ -311,6 +312,7 @@ void lv_ta_add_text(lv_obj_t * ta, const char * txt) /*Insert the text*/ lv_label_ins_text(ext->label, ext->cursor.pos, txt); + lv_ta_clear_selection(ta); if(ext->pwd_mode != 0) { ext->pwd_tmp = lv_mem_realloc(ext->pwd_tmp, strlen(ext->pwd_tmp) + strlen(txt) + 1); @@ -380,6 +382,7 @@ void lv_ta_del_char(lv_obj_t * ta) lv_txt_cut(label_txt, ext->cursor.pos - 1, 1); /*Refresh the label*/ lv_label_set_text(ext->label, label_txt); + lv_ta_clear_selection(ta); /*Don't let 'width == 0' because cursor will not be visible*/ if(lv_obj_get_width(ext->label) == 0) { @@ -415,6 +418,7 @@ void lv_ta_del_char_forward(lv_obj_t * ta) if(cp != lv_ta_get_cursor_pos(ta)) lv_ta_del_char(ta); } + /*===================== * Setter functions *====================*/ @@ -428,6 +432,9 @@ void lv_ta_set_text(lv_obj_t * ta, const char * txt) { lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + /*Clear the existing selection*/ + lv_ta_clear_selection(ta); + /*Add the character one-by-one if not all characters are accepted or there is character limit.*/ if(lv_ta_get_accepted_chars(ta) || lv_ta_get_max_length(ta)) { lv_label_set_text(ext->label, ""); @@ -623,10 +630,13 @@ void lv_ta_set_pwd_mode(lv_obj_t * ta, bool en) } txt[i] = '\0'; + lv_ta_clear_selection(ta); + lv_label_set_text(ext->label, NULL); } /*Pwd mode is now disabled*/ else if(ext->pwd_mode == 1 && en == false) { + lv_ta_clear_selection(ta); lv_label_set_text(ext->label, ext->pwd_tmp); lv_mem_free(ext->pwd_tmp); ext->pwd_tmp = NULL; @@ -778,6 +788,18 @@ void lv_ta_set_style(lv_obj_t * ta, lv_ta_style_t type, lv_style_t * style) } } +/** + * Enable/disable selection mode. + * @param ta pointer to a text area object + * @param en true or false to enable/disable selection mode + */ +void lv_ta_set_sel_mode(lv_obj_t *ta, bool en) { + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + ext->sel_mode = en; + if(!en) + lv_ta_clear_selection(ta); +} + /*===================== * Getter functions *====================*/ @@ -931,10 +953,73 @@ lv_style_t * lv_ta_get_style(const lv_obj_t * ta, lv_ta_style_t type) return style; } +/** + * Get the selection index of the text area. + * + * The last character is exclusive (i.e. if the API says that the selection + * ranges from 6 to 7, only character 6 is selected). + * @param ta Text area object + * @param sel_start pointer to int used to hold first selected character + * @param sel_end pointer to int used to hold last selected character + */ + +void lv_ta_get_selection(lv_obj_t * ta, int * sel_start, int * sel_end) { + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + lv_label_ext_t * ext_label = lv_obj_get_ext_attr(ext->label); + + /*Force both values to -1 if there is no selection*/ + if(ext_label->selection_start == -1 || ext_label->selection_end == -1) { + *sel_start = -1; + *sel_end = -1; + return; + } + + *sel_start = ext_label->selection_start; + *sel_end = ext_label->selection_end; +} + +/** + * Find whether text is selected or not. + * @param ta Text area object + * @return whether text is selected or not + */ +bool lv_ta_text_is_selected(const lv_obj_t *ta) { + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + lv_label_ext_t * ext_label = lv_obj_get_ext_attr(ext->label); + + return (ext_label->selection_start == -1 || ext_label->selection_end == -1); +} + +/** + * Find whether selection mode is enabled. + * @param ta pointer to a text area object + * @return true: selection mode is enabled, false: disabled + */ +bool lv_ta_get_sel_mode(lv_obj_t *ta, bool en) { + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + return ext->sel_mode; +} + /*===================== * Other functions *====================*/ +/** + * Clear the selection on the text area. + * @param ta Text area object + */ +void lv_ta_clear_selection(lv_obj_t * ta) { + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + lv_label_ext_t * ext_label = lv_obj_get_ext_attr(ext->label); + + if(ext_label->selection_start != -1 || ext_label->selection_end != -1) { + ext_label->selection_start = -1; + ext_label->selection_end = -1; + lv_obj_invalidate(ta); + } +} + /** * Move the cursor one character right * @param ta pointer to a text area object @@ -1103,7 +1188,7 @@ static bool lv_ta_scrollable_design(lv_obj_t * scrl, const lv_area_t * mask, lv_ cur_area.x1 += cur_style.body.padding.left; cur_area.y1 += cur_style.body.padding.top; - lv_draw_label(&cur_area, mask, &cur_style, opa_scale, letter_buf, LV_TXT_FLAG_NONE, 0); + lv_draw_label(&cur_area, mask, &cur_style, opa_scale, letter_buf, LV_TXT_FLAG_NONE, 0, -1, -1); } else if(ext->cursor.type == LV_CURSOR_OUTLINE) { cur_style.body.opa = LV_OPA_TRANSP; @@ -1234,8 +1319,9 @@ static lv_res_t lv_ta_signal(lv_obj_t * ta, lv_signal_t sign, void * param) } #endif } - else if(sign == LV_SIGNAL_PRESSED) { - update_cursor_position_on_click(ta, (lv_indev_t *) param); + else if(sign == LV_SIGNAL_PRESSED || sign == LV_SIGNAL_PRESSING + || sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_RELEASED) { + update_cursor_position_on_click(ta, sign, (lv_indev_t *) param); } return res; } @@ -1279,8 +1365,9 @@ static lv_res_t lv_ta_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, void } } } - else if(sign == LV_SIGNAL_PRESSED) { - update_cursor_position_on_click(ta, (lv_indev_t *)param); + else if(sign == LV_SIGNAL_PRESSING || sign == LV_SIGNAL_PRESSED || sign == LV_SIGNAL_PRESS_LOST || + sign == LV_SIGNAL_RELEASED) { + update_cursor_position_on_click(ta, sign, (lv_indev_t *)param); } return res; @@ -1535,7 +1622,7 @@ static void placeholder_update(lv_obj_t * ta) } } -static void update_cursor_position_on_click(lv_obj_t * ta, lv_indev_t * click_source) +static void update_cursor_position_on_click(lv_obj_t * ta, lv_signal_t sign, lv_indev_t * click_source) { if(click_source == NULL) return; @@ -1547,14 +1634,20 @@ static void update_cursor_position_on_click(lv_obj_t * ta, lv_indev_t * click_so lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + lv_label_ext_t * ext_label = lv_obj_get_ext_attr(ext->label); lv_area_t label_coords; + bool click_outside_label; uint16_t index_of_char_at_position; lv_obj_get_coords(ext->label, &label_coords); - lv_point_t point_act; + lv_point_t point_act, vect_act; + lv_indev_get_point(click_source, &point_act); + + lv_indev_get_vect(click_source, &vect_act); + if(point_act.x < 0 || point_act.y < 0) return; /*Ignore event from keypad*/ lv_point_t relative_position; relative_position.x = point_act.x - label_coords.x1; @@ -1562,19 +1655,71 @@ static void update_cursor_position_on_click(lv_obj_t * ta, lv_indev_t * click_so lv_coord_t label_width = lv_obj_get_width(ext->label); + + /*Check if the click happened on the left side of the area outside the label*/ if (relative_position.x < 0) { index_of_char_at_position = 0; + click_outside_label = true; } /*Check if the click happened on the right side of the area outside the label*/ else if (relative_position.x >= label_width) { index_of_char_at_position = LV_TA_CURSOR_LAST; + click_outside_label = true; } else { index_of_char_at_position = lv_label_get_letter_on(ext->label, &relative_position); + click_outside_label = !lv_label_is_char_under_pos(ext->label, &relative_position); } - lv_ta_set_cursor_pos(ta, index_of_char_at_position); + if(ext->sel_mode && !ext->selecting && !click_outside_label && sign == LV_SIGNAL_PRESSED) { + /*Input device just went down. Store the selection start position*/ + ext->tmp_sel_start = index_of_char_at_position; + ext->tmp_sel_end = -1; + ext->selecting = 1; + lv_obj_set_drag(lv_page_get_scrl(ta), false); + } else if(ext->selecting && sign == LV_SIGNAL_PRESSING) { + /*Input device may be moving. Store the end position */ + ext->tmp_sel_end = index_of_char_at_position; + } else if(ext->selecting && (sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_RELEASED)) { + /*Input device is released. Check if anything was selected.*/ + lv_obj_set_drag(lv_page_get_scrl(ta), true); + } + + if(ext->selecting || sign == LV_SIGNAL_PRESSED) + lv_ta_set_cursor_pos(ta, index_of_char_at_position); + + if(ext->selecting) { + /*If the selected area has changed then update the real values and*/ + /*invalidate the text area.*/ + if(ext->tmp_sel_start > ext->tmp_sel_end) { + if(ext_label->selection_start != ext->tmp_sel_end || + ext_label->selection_end != ext->tmp_sel_start) { + ext_label->selection_start = ext->tmp_sel_end; + ext_label->selection_end = ext->tmp_sel_start; + lv_obj_invalidate(ta); + } + } else if(ext->tmp_sel_start < ext->tmp_sel_end) { + if(ext_label->selection_start != ext->tmp_sel_start || + ext_label->selection_end != ext->tmp_sel_end) { + ext_label->selection_start = ext->tmp_sel_start; + ext_label->selection_end = ext->tmp_sel_end; + lv_obj_invalidate(ta); + } + } else { + if(ext_label->selection_start != -1 || ext_label->selection_end != -1) { + ext_label->selection_start = -1; + ext_label->selection_end = -1; + lv_obj_invalidate(ta); + } + } + /*Finish selection if necessary */ + if(sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_RELEASED) { + ext->selecting = 0; + } + } + + } #endif diff --git a/src/lv_objx/lv_ta.h b/src/lv_objx/lv_ta.h index b31afdaab..fa606e816 100644 --- a/src/lv_objx/lv_ta.h +++ b/src/lv_objx/lv_ta.h @@ -74,6 +74,10 @@ typedef struct lv_cursor_type_t type:4; /*Shape of the cursor*/ uint8_t state :1; /*Indicates that the cursor is visible now or not (Handled by the library)*/ } cursor; + int tmp_sel_start; /*Temporary value*/ + int tmp_sel_end; /*Temporary value*/ + uint8_t selecting :1; /*User is in process of selecting */ + uint8_t sel_mode :1; /*Text can be selected on this text area*/ } lv_ta_ext_t; enum { @@ -248,6 +252,13 @@ static inline void lv_ta_set_edge_flash(lv_obj_t * ta, bool en) */ void lv_ta_set_style(lv_obj_t *ta, lv_ta_style_t type, lv_style_t *style); +/** + * Enable/disable selection mode. + * @param ta pointer to a text area object + * @param en true or false to enable/disable selection mode + */ +void lv_ta_set_sel_mode(lv_obj_t *ta, bool en); + /*===================== * Getter functions *====================*/ @@ -360,10 +371,42 @@ static inline bool lv_ta_get_edge_flash(lv_obj_t * ta) */ lv_style_t * lv_ta_get_style(const lv_obj_t *ta, lv_ta_style_t type); +/** + * Get the selection index of the text area. + * + * The last character is exclusive (i.e. if the API says that the selection + * ranges from 6 to 7, only character 6 is selected). + * @param ta Text area object + * @param sel_start pointer to int used to hold first selected character + * @param sel_end pointer to int used to hold last selected character + */ + +void lv_ta_get_selection(lv_obj_t * ta, int * sel_start, int * sel_end); + +/** + * Find whether text is selected or not. + * @param ta Text area object + * @return whether text is selected or not + */ +bool lv_ta_text_is_selected(const lv_obj_t *ta); + +/** + * Find whether selection mode is enabled. + * @param ta pointer to a text area object + * @return true: selection mode is enabled, false: disabled + */ +bool lv_ta_get_sel_mode(lv_obj_t *ta, bool en); + /*===================== * Other functions *====================*/ +/** + * Clear the selection on the text area. + * @param ta Text area object + */ +void lv_ta_clear_selection(lv_obj_t * ta); + /** * Move the cursor one character right * @param ta pointer to a text area object diff --git a/src/lv_objx/lv_table.c b/src/lv_objx/lv_table.c index d3e15e93c..43b9c8352 100644 --- a/src/lv_objx/lv_table.c +++ b/src/lv_objx/lv_table.c @@ -701,7 +701,7 @@ static bool lv_table_design(lv_obj_t * table, const lv_area_t * mask, lv_design_ bool label_mask_ok; label_mask_ok = lv_area_intersect(&label_mask, mask, &cell_area); if(label_mask_ok) { - lv_draw_label(&txt_area, &label_mask, cell_style, opa_scale, ext->cell_data[cell] + 1, txt_flags, NULL); + lv_draw_label(&txt_area, &label_mask, cell_style, opa_scale, ext->cell_data[cell] + 1, txt_flags, NULL, -1, -1); } /*Draw lines after '\n's*/ lv_point_t p1;