Properly handle selection on multi-line text areas
This commit is contained in:
@@ -546,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;
|
||||
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
|
||||
|
||||
@@ -230,6 +230,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
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include <stdio.h>
|
||||
#include "lv_ta.h"
|
||||
#if LV_USE_TA != 0
|
||||
#include <string.h>
|
||||
@@ -1625,12 +1626,17 @@ static void update_cursor_position_on_click(lv_obj_t * ta, lv_signal_t sign, lv_
|
||||
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;
|
||||
@@ -1638,56 +1644,71 @@ static void update_cursor_position_on_click(lv_obj_t * ta, lv_signal_t sign, lv_
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if(!ext->selecting && sign == LV_SIGNAL_PRESSED) {
|
||||
if(!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(sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_RELEASED) {
|
||||
} else if(ext->selecting && (sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_RELEASED)) {
|
||||
/*Input device is released. Check if anything was selected.*/
|
||||
ext->selecting = 0;
|
||||
lv_obj_set_drag(lv_page_get_scrl(ta), true);
|
||||
}
|
||||
|
||||
/*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);
|
||||
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;
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
lv_ta_set_cursor_pos(ta, index_of_char_at_position);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user