mirror of
https://github.com/lvgl/lvgl.git
synced 2024-11-23 09:43:41 +08:00
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:
parent
651f69fd47
commit
7f57f37560
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*====================*/
|
||||
|
@ -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*/
|
||||
|
BIN
tests/ref_imgs/widgets/span_09.png
Normal file
BIN
tests/ref_imgs/widgets/span_09.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user