feat(spangroup): add lv_spangroup_get_span_by_point in spangroup (#6579)

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
Benign X 2024-09-12 17:56:35 +08:00 committed by GitHub
parent 651f69fd47
commit 7f57f37560
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 302 additions and 2 deletions

View File

@ -1,8 +1,19 @@
#include "../../lv_examples.h"
#if LV_USE_SPAN && LV_BUILD_EXAMPLES
static void click_event_cb(lv_event_t * e)
{
lv_obj_t * spans = lv_event_get_target(e);
lv_indev_t * indev = lv_event_get_indev(e);
lv_point_t point;
lv_indev_get_point(indev, &point);
lv_span_t * span = lv_spangroup_get_span_by_point(spans, &point);
LV_LOG_USER("%s", span ? lv_span_get_text(span) : "NULL");
}
/**
* Create span.
* Create spans and get clicked one
*/
void lv_example_span_1(void)
{
@ -17,6 +28,7 @@ void lv_example_span_1(void)
lv_obj_set_height(spans, 300);
lv_obj_center(spans);
lv_obj_add_style(spans, &style, 0);
lv_obj_add_flag(spans, LV_OBJ_FLAG_CLICKABLE);
lv_spangroup_set_align(spans, LV_TEXT_ALIGN_LEFT);
lv_spangroup_set_overflow(spans, LV_SPAN_OVERFLOW_CLIP);
@ -53,6 +65,8 @@ void lv_example_span_1(void)
lv_style_set_text_decor(lv_span_get_style(span), LV_TEXT_DECOR_STRIKETHROUGH);
lv_spangroup_refr_mode(spans);
lv_obj_add_event_cb(spans, click_event_cb, LV_EVENT_CLICKED, NULL);
}
#endif

View File

@ -69,6 +69,9 @@ static void lv_snippet_push(lv_snippet_t * item);
static lv_snippet_t * lv_get_snippet(uint32_t index);
static int32_t convert_indent_pct(lv_obj_t * spans, int32_t width);
static lv_span_coords_t make_span_coords(const lv_span_t * prev_span, const lv_span_t * curr_span, int32_t width,
lv_area_t padding, int32_t indent);
/**********************
* STATIC VARIABLES
**********************/
@ -258,6 +261,11 @@ lv_style_t * lv_span_get_style(lv_span_t * span)
return &span->style;
}
const char * lv_span_get_text(lv_span_t * span)
{
return span->txt;
}
lv_span_t * lv_spangroup_get_child(const lv_obj_t * obj, int32_t id)
{
if(obj == NULL) {
@ -451,6 +459,7 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width)
lv_snippet_t snippet; /* use to save cur_span info and push it to stack */
lv_memset(&snippet, 0, sizeof(snippet));
lv_span_t * prev_span = cur_span;
int32_t line_cnt = 0;
int32_t lines = spans->lines < 0 ? INT32_MAX : spans->lines;
/* the loop control how many lines need to draw */
@ -462,6 +471,8 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width)
while(1) {
/* switch to the next span when current is end */
if(cur_txt[cur_txt_ofs] == '\0') {
cur_span->trailing_pos = txt_pos;
cur_span = lv_ll_get_next(&spans->child_ll, cur_span);
if(cur_span == NULL) break;
cur_txt = cur_span->txt;
@ -484,6 +495,8 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width)
int32_t use_width = 0;
bool isfill = lv_text_get_snippet(&cur_txt[cur_txt_ofs], snippet.font, snippet.letter_space,
max_w, txt_flag, &use_width, &next_ofs);
if(isfill) txt_pos.x = 0;
else txt_pos.x += use_width;
/* break word deal width */
if(isfill && next_ofs > 0 && snippet_cnt > 0) {
@ -521,8 +534,16 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width)
}
/* next line init */
txt_pos.x = 0;
txt_pos.y += max_line_h;
/* iterate all the spans in the current line and set the trailing height to the max line height */
for(lv_span_t * tmp_span = prev_span;
tmp_span && tmp_span != cur_span;
tmp_span = lv_ll_get_next(&spans->child_ll, tmp_span))
tmp_span->trailing_height = max_line_h;
prev_span = cur_span;
max_w = max_width;
line_cnt += 1;
if(line_cnt >= lines) {
@ -534,6 +555,69 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width)
return txt_pos.y;
}
lv_span_coords_t lv_spangroup_get_span_coords(lv_obj_t * obj, const lv_span_t * span)
{
/* find previous span */
const lv_spangroup_t * spangroup = (lv_spangroup_t *)obj;
const lv_ll_t * spans = &spangroup->child_ll;
const int32_t width = lv_obj_get_content_width(obj);
const int32_t indent = lv_spangroup_get_indent(obj);
if(obj == NULL || span == NULL || lv_ll_get_head(spans) == NULL) return (lv_span_coords_t) {
0
};
lv_span_t * prev_span = NULL;
lv_span_t * curr_span;
LV_LL_READ(spans, curr_span) {
if(curr_span == span) break;
prev_span = curr_span;
}
const uint32_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
return make_span_coords(prev_span, curr_span, width, (lv_area_t) {
.x1 = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width,
.y1 = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width,
.x2 = lv_obj_get_style_pad_right(obj, LV_PART_MAIN) + border_width, .y2 = 0
},
indent);
}
lv_span_t * lv_spangroup_get_span_by_point(lv_obj_t * obj, const lv_point_t * p)
{
const lv_spangroup_t * spangroup = (lv_spangroup_t *)obj;
const lv_ll_t * spans = &spangroup->child_ll;
const int32_t width = lv_obj_get_content_width(obj);
const int32_t indent = lv_spangroup_get_indent(obj);
if(obj == NULL || p == NULL || lv_ll_get_head(spans) == NULL) return NULL;
lv_point_t point;
point.x = p->x - obj->coords.x1;
point.y = p->y - obj->coords.y1;
/* find previous span */
const lv_span_t * prev_span = NULL;
lv_span_t * curr_span;
LV_LL_READ(spans, curr_span) {
lv_span_coords_t coords = make_span_coords(prev_span, curr_span, width, (lv_area_t) {
.x1 = lv_obj_get_style_pad_left(obj, LV_PART_MAIN),
.y1 = lv_obj_get_style_pad_top(obj, LV_PART_MAIN),
.x2 = lv_obj_get_style_pad_right(obj, LV_PART_MAIN),
.y2 = 0
},
indent);
if(lv_area_is_point_on(&coords.heading, &point, 0) ||
lv_area_is_point_on(&coords.middle, &point, 0) ||
lv_area_is_point_on(&coords.trailing, &point, 0)) {
return curr_span;
}
prev_span = curr_span;
}
return NULL;
}
/**********************
* STATIC FUNCTIONS
**********************/
@ -1086,4 +1170,52 @@ static void refresh_self_size(lv_obj_t * obj)
lv_obj_refresh_self_size(obj);
}
static lv_span_coords_t make_span_coords(const lv_span_t * prev_span, const lv_span_t * curr_span, const int32_t width,
const lv_area_t padding, const int32_t indent)
{
lv_span_coords_t coords = { 0 };
if(curr_span == NULL) return coords;
/* first line */
if(prev_span == NULL) {
lv_area_set(&coords.heading, padding.x1 + indent, padding.y1, width + padding.x1,
curr_span->trailing_pos.y + padding.y1);
lv_area_set(&coords.middle, coords.heading.x1, coords.heading.y2, curr_span->trailing_pos.x + padding.x1,
coords.heading.y2 + curr_span->trailing_height);
lv_area_set(&coords.trailing, 0, 0, 0, 0);
return coords;
}
/* start and end on the same line */
const bool is_same_line = prev_span->trailing_pos.y == curr_span->trailing_pos.y;
if(is_same_line == true) {
lv_area_set(&coords.heading,
prev_span->trailing_pos.x + padding.x1, prev_span->trailing_pos.y + padding.y1,
curr_span->trailing_pos.x + padding.x1, curr_span->trailing_pos.y + curr_span->trailing_height + padding.y1);
return coords;
}
/* common case */
const lv_point_t pre_trailing_pos = prev_span->trailing_pos;
const int32_t pre_trailing_height = prev_span->trailing_height;
lv_area_set(&coords.heading,
pre_trailing_pos.x + padding.x1, pre_trailing_pos.y + padding.y1,
width + padding.x1, pre_trailing_pos.y + pre_trailing_height + padding.y1);
/* When it happens to be two lines of text,
* the y2 of the middle area is exactly the y1 + line height of the first line of text,
* so the area of the middle area is empty.
* */
lv_area_set(&coords.middle,
padding.x1, coords.heading.y2,
width + padding.x1, curr_span->trailing_pos.y + padding.y1);
lv_area_set(&coords.trailing,
coords.middle.x1, coords.middle.y2,
curr_span->trailing_pos.x + padding.x1, curr_span->trailing_pos.y + curr_span->trailing_height + padding.y1);
return coords;
}
#endif

View File

@ -41,6 +41,13 @@ typedef enum {
LV_SPAN_MODE_LAST /**< Fence member */
} lv_span_mode_t;
/** Coords of a span */
typedef struct _lv_span_coords_t {
lv_area_t heading;
lv_area_t middle;
lv_area_t trailing;
} lv_span_coords_t;
LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_spangroup_class;
/**********************
@ -136,6 +143,13 @@ void lv_spangroup_set_max_lines(lv_obj_t * obj, int32_t lines);
*/
lv_style_t * lv_span_get_style(lv_span_t * span);
/**
* Get a pointer to the text of a span
* @param span pointer to the span
* @return pointer to the text
*/
const char * lv_span_get_text(lv_span_t * span);
/**
* Get a spangroup child by its index.
*
@ -214,6 +228,39 @@ uint32_t lv_spangroup_get_expand_width(lv_obj_t * obj, uint32_t max_width);
*/
int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width);
/**
* Get the span's coords in the spangroup.
* @note Before calling this function, please make sure that the layout of span group has been updated.
* Like calling lv_obj_update_layout() like function.
*
* +--------+
* |Heading +--->------------------+
* | Pos | | Heading |
* +--------+---+------------------+
* | |
* | |
* | |
* | Middle +--------+|
* | |Trailing||
* | +-| Pos ||
* | | +--------+|
* +-------------------v-----------+
* | Trailing |
* +-------------------+
* @param obj pointer to a spangroup object.
* @param span pointer to a span.
* @return the span's coords in the spangroup.
*/
lv_span_coords_t lv_spangroup_get_span_coords(lv_obj_t * obj, const lv_span_t * span);
/**
* Get the span object by point.
* @param obj pointer to a spangroup object.
* @param point pointer to point containing absolute coordinates
* @return pointer to the span under the point or `NULL` if not found.
*/
lv_span_t * lv_spangroup_get_span_by_point(lv_obj_t * obj, const lv_point_t * point);
/*=====================
* Other functions
*====================*/

View File

@ -32,6 +32,9 @@ struct _lv_span_t {
lv_obj_t * spangroup; /**< a pointer to spangroup */
lv_style_t style; /**< display text style */
uint32_t static_flag : 1; /**< the text is static flag */
lv_point_t trailing_pos;
int32_t trailing_height;
};
/** Data of label*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -375,4 +375,108 @@ void test_spangroup_style_text_letter_space(void)
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/span_08.png");
}
#if LV_FONT_MONTSERRAT_24 && LV_FONT_MONTSERRAT_20
void test_spangroup_get_span_coords(void)
{
/* Initialize the active screen and create a new span group */
active_screen = lv_screen_active();
spangroup = lv_spangroup_create(active_screen);
const uint32_t span_count = 5;
lv_span_t * spans[span_count];
/* Set styles and properties for the span group */
lv_obj_set_style_outline_width(spangroup, 1, 0);
lv_spangroup_set_indent(spangroup, 20);
lv_spangroup_set_mode(spangroup, LV_SPAN_MODE_BREAK);
lv_obj_set_width(spangroup, 300);
lv_obj_set_style_pad_all(spangroup, 20, LV_PART_MAIN);
/* Create spans and set their properties */
spans[0] = lv_spangroup_new_span(spangroup);
lv_span_set_text(spans[0], "China is a beautiful country.");
lv_style_set_text_color(lv_span_get_style(spans[0]), lv_palette_main(LV_PALETTE_RED));
lv_style_set_text_decor(lv_span_get_style(spans[0]), LV_TEXT_DECOR_UNDERLINE);
lv_style_set_text_opa(lv_span_get_style(spans[0]), LV_OPA_50);
spans[1] = lv_spangroup_new_span(spangroup);
lv_span_set_text_static(spans[1], "good good study, day day up.");
lv_style_set_text_font(lv_span_get_style(spans[1]), &lv_font_montserrat_24);
lv_style_set_text_color(lv_span_get_style(spans[1]), lv_palette_main(LV_PALETTE_GREEN));
spans[2] = lv_spangroup_new_span(spangroup);
lv_span_set_text_static(spans[2], "LVGL is an open-source graphics library.");
lv_style_set_text_color(lv_span_get_style(spans[2]), lv_palette_main(LV_PALETTE_BLUE));
spans[3] = lv_spangroup_new_span(spangroup);
lv_span_set_text_static(spans[3], "the boy no name.");
lv_style_set_text_color(lv_span_get_style(spans[3]), lv_palette_main(LV_PALETTE_GREEN));
lv_style_set_text_font(lv_span_get_style(spans[3]), &lv_font_montserrat_20);
lv_style_set_text_decor(lv_span_get_style(spans[3]), LV_TEXT_DECOR_UNDERLINE);
spans[4] = lv_spangroup_new_span(spangroup);
lv_span_set_text(spans[4], "I have a dream that hope to come true.");
lv_style_set_text_decor(lv_span_get_style(spans[4]), LV_TEXT_DECOR_STRIKETHROUGH);
/* Refresh the span group mode and update layout */
lv_spangroup_refr_mode(spangroup);
lv_obj_update_layout(spangroup);
/* Define expected coordinates for testing */
const lv_span_coords_t test_coords[] = {
{.heading = {.x1 = 40, .y1 = 20, .x2 = 280, .y2 = 20}, .middle = {.x1 = 40, .y1 = 20, .x2 = 241, .y2 = 36}, .trailing = {.x1 = 0, .y1 = 0, .x2 = 0, .y2 = 0}},
{.heading = {.x1 = 241, .y1 = 20, .x2 = 280, .y2 = 36}, .middle = {.x1 = 20, .y1 = 36, .x2 = 280, .y2 = 63}, .trailing = {.x1 = 20, .y1 = 63, .x2 = 155, .y2 = 90}},
{.heading = {.x1 = 155, .y1 = 63, .x2 = 280, .y2 = 90}, .middle = {.x1 = 20, .y1 = 90, .x2 = 280, .y2 = 90}, .trailing = {.x1 = 20, .y1 = 90, .x2 = 188, .y2 = 112}},
{.heading = {.x1 = 188, .y1 = 90, .x2 = 280, .y2 = 112}, .middle = {.x1 = 20, .y1 = 112, .x2 = 280, .y2 = 112}, .trailing = {.x1 = 20, .y1 = 112, .x2 = 116, .y2 = 134}},
{.heading = {.x1 = 116, .y1 = 112, .x2 = 280, .y2 = 134}, .middle = {.x1 = 20, .y1 = 134, .x2 = 280, .y2 = 134}, .trailing = {.x1 = 20, .y1 = 134, .x2 = 160, .y2 = 150}}
};
/* Define colors for visual testing */
const lv_color_t colors[] = {
lv_palette_main(LV_PALETTE_RED), lv_palette_main(LV_PALETTE_GREEN), lv_palette_main(LV_PALETTE_BLUE),
lv_palette_main(LV_PALETTE_YELLOW), lv_palette_main(LV_PALETTE_PURPLE), lv_palette_main(LV_PALETTE_ORANGE),
lv_palette_main(LV_PALETTE_INDIGO), lv_palette_main(LV_PALETTE_BROWN), lv_palette_main(LV_PALETTE_GREY),
lv_palette_main(LV_PALETTE_PINK)
};
const uint32_t color_count = sizeof(colors) / sizeof(colors[0]);
const lv_area_t area = spangroup->coords;
/* Iterate through spans and validate coordinates */
for(uint32_t i = 0; i < span_count; i++) {
lv_span_coords_t coords = lv_spangroup_get_span_coords(spangroup, spans[i]);
TEST_ASSERT_EQUAL_MEMORY(&coords.heading, &test_coords[i].heading, sizeof(lv_span_coords_t));
/* Visual testing */
const lv_color_t color = colors[i % color_count];
/* Create and style heading object */
lv_obj_t * obj_head = lv_obj_create(active_screen);
lv_obj_remove_style_all(obj_head);
lv_obj_set_pos(obj_head, coords.heading.x1 + area.x1, coords.heading.y1 + area.y1);
lv_obj_set_size(obj_head, coords.heading.x2 - coords.heading.x1, coords.heading.y2 - coords.heading.y1);
lv_obj_set_style_bg_color(obj_head, color, LV_PART_MAIN);
lv_obj_set_style_bg_opa(obj_head, LV_OPA_50, LV_PART_MAIN);
/* Create and style middle object */
lv_obj_t * obj_middle = lv_obj_create(active_screen);
lv_obj_remove_style_all(obj_middle);
lv_obj_set_pos(obj_middle, coords.middle.x1 + area.x1, coords.middle.y1 + area.y1);
lv_obj_set_size(obj_middle, coords.middle.x2 - coords.middle.x1, coords.middle.y2 - coords.middle.y1);
lv_obj_set_style_bg_color(obj_middle, color, LV_PART_MAIN);
lv_obj_set_style_bg_opa(obj_middle, LV_OPA_50, LV_PART_MAIN);
/* Create and style trailing object */
lv_obj_t * obj_trailing = lv_obj_create(active_screen);
lv_obj_remove_style_all(obj_trailing);
lv_obj_set_pos(obj_trailing, coords.trailing.x1 + area.x1, coords.trailing.y1 + area.y1);
lv_obj_set_size(obj_trailing, coords.trailing.x2 - coords.trailing.x1, coords.trailing.y2 - coords.trailing.y1);
lv_obj_set_style_bg_color(obj_trailing, color, LV_PART_MAIN);
lv_obj_set_style_bg_opa(obj_trailing, LV_OPA_50, LV_PART_MAIN);
}
/* Validate the final screenshot */
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/span_09.png");
}
#endif
#endif