Properly handle selection on multi-line text areas

This commit is contained in:
Themba Dube
2019-03-27 19:11:04 -04:00
parent 3b747718ea
commit e63e1f230a
3 changed files with 128 additions and 26 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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