From 8f87a2411a674fe985e76ef62bdcc555c535ce06 Mon Sep 17 00:00:00 2001 From: Ali Alzyod Date: Thu, 12 Dec 2019 14:22:45 +0900 Subject: [PATCH] evas_textblock: content fit feature Summary: **Content Fit Feature for Evas_Object_Textblock** This Feature is available at **Evas **object level. And **Edje **level (where it is internally use evas functionality) This feature will allow text block to fit its content font size to proper size to fit its area. **Main Properties:** Fit Modes : None=Default, Width, Height, All [Width+Height] Fit Size Range : Contains maximum and minimum font size to be used (and in between). Fit Step Size : Step(Jump) value when trying fonts sizes between Size_Range max and min. Fit Size Array : Other way to resize font, where you explicitly select font sizes to be uses (for example [20, 50, 100] it will try 3 sizes only) Text Fit feature was available in Edje but: 1- It doesn't effected by ellipsis or warping in font style (or do not handle the in right way) 2- Accuracy is not good (specially if you have fix pixel size elements (spaces,tabs,items)) 3- No (Step size, Size Array) available. Test Plan: To check the Feature > elementary_test > fit > textbock fit You can modify all the modes and properties These are two examples, One using Evas other uses Edje **Evas** ``` #include enum BUTTON{ BUTTON_MODE = 0, BUTTON_MAX = 1, BUTTON_MIN = 2, BUTTON_STEP = 3, BUTTON_ARRAY = 4, BUTTON_CONTENT = 5, BUTTON_STYLE = 6, BUTTON_ALL = BUTTON_STYLE+1, }; char* BUTTON_STR[BUTTON_ALL] ={ "MODE", "MAX", "MIN", "STEP", "ARRAY", "CONTENT", "STYLE", }; char *contents[] = { "Hello World", "This is Line
THis is other Line", "This text contains SPECIFIC SIZE that does not effected by fit mode" }; char *styles[] = { "DEFAULT='font=sans font_size=30 color=#000 wrap=mixed ellipsis=1.0'", "DEFAULT='font=sans font_size=30 color=#000 wrap=mixed'", "DEFAULT='font=sans font_size=30 color=#000 ellipsis=1.0'", "DEFAULT='font=sans font_size=30 color=#000'", }; char *styles_names[] = { "wrap=mixed ellipsis=1.0", "wrap=mixed ellipsis=NONE", "wrap=NONE ellipsis=1.0", "wrap=NONE ellipsis=NONE", }; typedef struct _APP { Evas_Object *win, *box, *txtblock,*bg, *boxHor, *boxHor2; Eo *btn[BUTTON_ALL]; Eo *lbl_status; char * str; unsigned int i_contnet, i_style; } APP; APP *app; char * get_fit_status(Eo * textblock); static void _btn_clicked(void *data EINA_UNUSED, Eo *obj, void *eventInfo EINA_UNUSED){ if (obj == app->btn[BUTTON_MODE]) { unsigned int options; evas_textblock_fit_options_get(app->txtblock, &options); if (options == TEXTBLOCK_FIT_MODE_NONE) evas_textblock_fit_options_set(app->txtblock, TEXTBLOCK_FIT_MODE_HEIGHT); else if (options == TEXTBLOCK_FIT_MODE_HEIGHT) evas_textblock_fit_options_set(app->txtblock, TEXTBLOCK_FIT_MODE_WIDTH); else if (options == TEXTBLOCK_FIT_MODE_WIDTH) evas_textblock_fit_options_set(app->txtblock, TEXTBLOCK_FIT_MODE_ALL); else if (options == TEXTBLOCK_FIT_MODE_ALL) evas_textblock_fit_options_set(app->txtblock, TEXTBLOCK_FIT_MODE_NONE); } else if (obj == app->btn[BUTTON_MAX]) { unsigned int min, max; evas_textblock_fit_size_range_get(app->txtblock, &min, &max); max -= 5; evas_textblock_fit_size_range_set(app->txtblock, min, max); } else if (obj == app->btn[BUTTON_MIN]) { unsigned int min, max; evas_textblock_fit_size_range_get(app->txtblock, &min, &max); min += 5; evas_textblock_fit_size_range_set(app->txtblock, min, max); } else if (obj == app->btn[BUTTON_STEP]) { unsigned int step; evas_textblock_fit_step_size_get(app->txtblock, &step); step++; evas_textblock_fit_step_size_set(app->txtblock, step); } else if (obj == app->btn[BUTTON_ARRAY]) { unsigned int font_size[] = {10, 50, 100 ,150}; evas_textblock_fit_size_array_set(app->txtblock,font_size,4); } else if (obj == app->btn[BUTTON_CONTENT]) { app->i_contnet++; if(app->i_contnet>=sizeof(contents)/sizeof(char*)) app->i_contnet=0; evas_object_textblock_text_markup_set(app->txtblock,contents[app->i_contnet]); } else if (obj == app->btn[BUTTON_STYLE]) { app->i_style++; if(app->i_style>=sizeof(styles)/sizeof(char*)) app->i_style=0; Evas_Textblock_Style *style = evas_object_textblock_style_get(app->txtblock); evas_textblock_style_set(style,styles[app->i_style]); } elm_object_text_set(app->lbl_status, get_fit_status(app->txtblock)); } char * get_fit_status(Eo * textblock) { static char status[0xFFF]; unsigned int options,min,max,step,size_array[256]; size_t size_array_len; evas_textblock_fit_options_get(textblock,&options); evas_textblock_fit_size_range_get(textblock,&min,&max); evas_textblock_fit_step_size_get(textblock,&step); evas_textblock_fit_size_array_get(textblock,NULL,&size_array_len,0); if (size_array_len>255) size_array_len = 255; evas_textblock_fit_size_array_get(textblock,size_array,NULL,size_array_len); strcpy(status,"Mode : "); if (options == TEXTBLOCK_FIT_MODE_NONE) strcat(status,"MODE_NONE"); else if (options == TEXTBLOCK_FIT_MODE_HEIGHT) strcat(status,"MODE_HEIGHT"); else if (options == TEXTBLOCK_FIT_MODE_WIDTH) strcat(status,"MODE_WIDTH"); else if (options == TEXTBLOCK_FIT_MODE_ALL) strcat(status,"MODE_ALL"); strcat(status,"
"); sprintf(status + strlen(status),"Max : %d
",max); sprintf(status + strlen(status),"Min : %d
",min); sprintf(status + strlen(status),"Step : %d
",step); sprintf(status + strlen(status),"Array : [ "); for (size_t i = 0 ; i < 10 ; i++) { if(i"); sprintf(status + strlen(status),"%s",styles_names[app->i_style]); return status; } int elm_main(int argc, char **argv) { app = calloc(sizeof(APP), 1); elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); app->win = elm_win_util_standard_add("Main", "App"); elm_win_autodel_set(app->win, EINA_TRUE); app->box = elm_box_add(app->win); app->boxHor = elm_box_add(app->box); app->boxHor2 = elm_box_add(app->box); app->txtblock = evas_object_textblock_add(app->box); app->bg = elm_bg_add(app->box); elm_bg_color_set(app->bg,255,255,255); Evas_Textblock_Style *style = evas_textblock_style_new(); evas_textblock_style_set(style,styles[0]); evas_object_textblock_style_set(app->txtblock,style); evas_object_textblock_text_markup_set(app->txtblock,contents[0]); elm_box_horizontal_set(app->boxHor, EINA_TRUE); elm_box_horizontal_set(app->boxHor2, EINA_TRUE); evas_object_size_hint_weight_set(app->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(app->box, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_size_hint_weight_set(app->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(app->box, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_show(app->txtblock); evas_object_show(app->bg); evas_object_show(app->box); evas_object_show(app->boxHor); evas_object_show(app->boxHor2); elm_box_pack_end(app->box, app->bg); elm_box_pack_end(app->box, app->boxHor); elm_box_pack_end(app->box, app->boxHor2); elm_object_content_set(app->bg,app->txtblock); elm_win_resize_object_add(app->win, app->box); evas_object_resize(app->win, 320, 480); for(int i = 0 ; i < BUTTON_ALL ; i++) { app->btn[i] = elm_button_add(app->boxHor); evas_object_smart_callback_add(app->btn[i], "clicked", _btn_clicked, NULL); elm_object_text_set(app->btn[i], BUTTON_STR[i]); elm_box_pack_end(app->boxHor, app->btn[i]); evas_object_show(app->btn[i]); } app->lbl_status = elm_label_add(app->boxHor2); elm_object_text_set(app->lbl_status, get_fit_status(app->txtblock)); elm_box_pack_end(app->boxHor2, app->lbl_status); evas_object_show(app->lbl_status); evas_object_size_hint_weight_set(app->txtblock, EVAS_HINT_EXPAND,EVAS_HINT_EXPAND); evas_object_size_hint_align_set(app->txtblock, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_size_hint_weight_set(app->bg, EVAS_HINT_EXPAND,EVAS_HINT_EXPAND); evas_object_size_hint_align_set(app->bg, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_show(app->win); elm_run(); return 0; } ELM_MAIN() ``` **Edje** ``` // compile: edje_cc source.edc // run: edje_player source.edje collections { styles { style { name: "text_style"; base: "font=sans font_size=30 color=#FFF wrap=mixed ellipsis=1.0"; tag: "br" "\n"; tag: "ps" "ps"; tag: "tab" "\t"; tag: "b" "+ font_weight=Bold"; } } group { name: "my_group"; // must be the same as in source.c parts { part { name: "background"; type: RECT; scale: 1; description { color: 0 0 0 0; rel1.relative: 0.0 0.0; rel2.relative: 1.0 1.0; } } part { name: "text"; type: TEXTBLOCK; scale: 1; entry_mode: NONE; effect: OUTLINE_SHADOW; description { state: "default" 0.0; rel1.to : "background"; rel1.relative: 0.0 0.0; rel2.to : "background"; rel2.relative: 1.0 1.0; text { style: "text_style"; align: 0.0 0.0; text: "Hello World This is Me"; fit: 1 1; fit_step: 1; size_range: 30 200; //fit_size_array: 20 40 60 80 100 200; } } } } } } ``` Found Task T5724 relative to this Feature Reviewers: woohyun, bowonryu, cedric, raster Reviewed By: woohyun Subscribers: a.srour, #committers, #reviewers, cedric Tags: #efl Differential Revision: https://phab.enlightenment.org/D9280 --- src/bin/edje/edje_cc_handlers.c | 81 ++++ src/bin/elementary/test.c | 2 + src/bin/elementary/test_label.c | 232 +++++++++ src/lib/edje/edje_data.c | 2 + src/lib/edje/edje_private.h | 3 + src/lib/edje/edje_textblock.c | 103 +--- src/lib/eet/Eet.h | 23 + src/lib/evas/canvas/evas_object_textblock.c | 496 +++++++++++++++++++- src/lib/evas/canvas/evas_textblock_legacy.h | 100 ++++ src/tests/evas/evas_test_textblock.c | 27 ++ 10 files changed, 979 insertions(+), 90 deletions(-) diff --git a/src/bin/edje/edje_cc_handlers.c b/src/bin/edje/edje_cc_handlers.c index c9bbc1744a..8353d260a3 100644 --- a/src/bin/edje/edje_cc_handlers.c +++ b/src/bin/edje/edje_cc_handlers.c @@ -419,6 +419,8 @@ static void st_collections_group_parts_part_description_text_repch(void); static void st_collections_group_parts_part_description_text_size(void); static void st_collections_group_parts_part_description_text_size_range(void); static void st_collections_group_parts_part_description_text_fit(void); +static void st_collections_group_parts_part_description_text_fit_step(void); +static void st_collections_group_parts_part_description_text_fit_size_array(void); static void st_collections_group_parts_part_description_text_min(void); static void st_collections_group_parts_part_description_text_max(void); static void st_collections_group_parts_part_description_text_align(void); @@ -952,6 +954,8 @@ New_Statement_Handler statement_handlers[] = {"collections.group.parts.part.description.text.size", st_collections_group_parts_part_description_text_size}, {"collections.group.parts.part.description.text.size_range", st_collections_group_parts_part_description_text_size_range}, {"collections.group.parts.part.description.text.fit", st_collections_group_parts_part_description_text_fit}, + {"collections.group.parts.part.description.text.fit_step", st_collections_group_parts_part_description_text_fit_step}, + {"collections.group.parts.part.description.text.fit_size_array", st_collections_group_parts_part_description_text_fit_size_array}, {"collections.group.parts.part.description.text.min", st_collections_group_parts_part_description_text_min}, {"collections.group.parts.part.description.text.max", st_collections_group_parts_part_description_text_max}, {"collections.group.parts.part.description.text.align", st_collections_group_parts_part_description_text_align}, @@ -11654,6 +11658,83 @@ st_collections_group_parts_part_description_text_fit(void) ed->text.fit_y = parse_bool(1); } + +/** + @page edcref + + @property + fit_step + @parameters + [font step size in points (pt)] + @effect + Sets the font step size for the text part. when fitting text + + Defaults: 1 + @since 1.24.0 + @endproperty + */ +static void +st_collections_group_parts_part_description_text_fit_step(void) +{ + Edje_Part_Description_Text *ed; + + check_arg_count(1); + + if (current_part->type != EDJE_PART_TYPE_TEXTBLOCK) + { + ERR("parse error %s:%i. text attributes in non-TEXTBLOCK part.", + file_in, line - 1); + exit(-1); + } + + ed = (Edje_Part_Description_Text *)current_desc; + + ed->text.fit_step = parse_int(0); + + if (ed->text.fit_step < 1) + { + ERR("parse error %s:%i. fit step less than 1.", + file_in, line - 1); + exit(-1); + } +} + +/** + @page edcref + + @property + fit + @parameters + [Array of font sizes in points] + @effect + Sets the allowed font sizes array for the text part. + @since 1.24.0 + @endproperty + */ +static void +st_collections_group_parts_part_description_text_fit_size_array(void) +{ + int n, argc; + Edje_Part_Description_Text *ed; + + if (current_part->type != EDJE_PART_TYPE_TEXTBLOCK) + { + ERR("parse error %s:%i. text attributes in non-TEXTBLOCK part.", + file_in, line - 1); + exit(-1); + } + + ed = (Edje_Part_Description_Text *)current_desc; + check_min_arg_count(1); + + for (n = 0, argc = get_arg_count(); n < argc; n++) + { + unsigned int *value = malloc(sizeof(unsigned int)); + *value = (unsigned int) parse_int(n); + ed->text.fit_size_array = eina_list_append(ed->text.fit_size_array, value); + } +} + /** @page edcref diff --git a/src/bin/elementary/test.c b/src/bin/elementary/test.c index cab1923fcf..966d164c24 100644 --- a/src/bin/elementary/test.c +++ b/src/bin/elementary/test.c @@ -240,6 +240,7 @@ void test_flip_page_eo(void *data, Evas_Object *obj, void *event_info); void test_label(void *data, Evas_Object *obj, void *event_info); void test_label_slide(void *data, Evas_Object *obj, void *event_info); void test_label_wrap(void *data, Evas_Object *obj, void *event_info); +void test_textblock_fit(void *data, Evas_Object *obj, void *event_info); void test_label_ellipsis(void *data, Evas_Object *obj, void *event_info); void test_label_colors(void *data, Evas_Object *obj, void *event_info); void test_label_emoji(void *data, Evas_Object *obj, void *event_info); @@ -1206,6 +1207,7 @@ add_tests: ADD_TEST(NULL, "Text", "Label", test_label); ADD_TEST(NULL, "Text", "Label Slide", test_label_slide); ADD_TEST(NULL, "Text", "Label Wrap", test_label_wrap); + ADD_TEST(NULL, "Text", "Textblock Fit", test_textblock_fit); ADD_TEST(NULL, "Text", "Label Ellipsis", test_label_ellipsis); ADD_TEST(NULL, "Text", "Label Colors", test_label_colors); ADD_TEST(NULL, "Text", "Label Emoji", test_label_emoji); diff --git a/src/bin/elementary/test_label.c b/src/bin/elementary/test_label.c index 233ce015b4..929d06b1d5 100644 --- a/src/bin/elementary/test_label.c +++ b/src/bin/elementary/test_label.c @@ -309,6 +309,238 @@ test_label_slide(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *eve evas_object_show(win); } + + +/*** FIT TEXT **************************************************************/ +enum BUTTON{ + BUTTON_MODE = 0, + BUTTON_MAX = 1, + BUTTON_MIN = 2, + BUTTON_STEP = 3, + BUTTON_ARRAY = 4, + BUTTON_CONTENT = 5, + BUTTON_STYLE = 6, + BUTTON_ALL = BUTTON_STYLE+1, +}; + +char* BUTTON_STR[BUTTON_ALL] ={ + "MODE", + "MAX", + "MIN", + "STEP", + "ARRAY", + "CONTENT", + "STYLE", +}; + +char *contents[] = { + "Hello World", + "This is Line
THis is other Line", + "This text contains SPECIFIC SIZEthat does not effected by fit mode" + }; + +char *styles[] = { + "DEFAULT='font=sans font_size=30 color=#000 wrap=mixed ellipsis=1.0'", + "DEFAULT='font=sans font_size=30 color=#000 wrap=mixed'", + "DEFAULT='font=sans font_size=30 color=#000 ellipsis=1.0'", + "DEFAULT='font=sans font_size=30 color=#000'", + }; + +char *styles_names[] = { + "wrap=mixed ellipsis=1.0", + "wrap=mixed ellipsis=NONE", + "wrap=NONE ellipsis=1.0", + "wrap=NONE ellipsis=NONE", + }; + +typedef struct _APP +{ + Evas_Object *win, *box, *txtblock,*bg, *boxHor, *boxHor2; + Eo *btn[BUTTON_ALL]; + Eo *lbl_status; + char * str; + unsigned int i_contnet, i_style; +} APP; +APP *app; + +char * get_fit_status(Eo * textblock); + +static void _btn_clicked(void *data EINA_UNUSED, Eo *obj, void *eventInfo EINA_UNUSED){ + if (obj == app->btn[BUTTON_MODE]) + { + unsigned int options; + evas_textblock_fit_options_get(app->txtblock, &options); + if (options == TEXTBLOCK_FIT_MODE_NONE) + evas_textblock_fit_options_set(app->txtblock, TEXTBLOCK_FIT_MODE_HEIGHT); + else if (options == TEXTBLOCK_FIT_MODE_HEIGHT) + evas_textblock_fit_options_set(app->txtblock, TEXTBLOCK_FIT_MODE_WIDTH); + else if (options == TEXTBLOCK_FIT_MODE_WIDTH) + evas_textblock_fit_options_set(app->txtblock, TEXTBLOCK_FIT_MODE_ALL); + else if (options == TEXTBLOCK_FIT_MODE_ALL) + evas_textblock_fit_options_set(app->txtblock, TEXTBLOCK_FIT_MODE_NONE); + } + else if (obj == app->btn[BUTTON_MAX]) + { + unsigned int min, max; + evas_textblock_fit_size_range_get(app->txtblock, &min, &max); + max -= 5; + evas_textblock_fit_size_range_set(app->txtblock, min, max); + } + else if (obj == app->btn[BUTTON_MIN]) + { + unsigned int min, max; + evas_textblock_fit_size_range_get(app->txtblock, &min, &max); + min += 5; + evas_textblock_fit_size_range_set(app->txtblock, min, max); + } + else if (obj == app->btn[BUTTON_STEP]) + { + unsigned int step; + evas_textblock_fit_step_size_get(app->txtblock, &step); + step++; + evas_textblock_fit_step_size_set(app->txtblock, step); + } + else if (obj == app->btn[BUTTON_ARRAY]) + { + unsigned int font_size[] = {10, 50, 100 ,150}; + evas_textblock_fit_size_array_set(app->txtblock,font_size,4); + } + else if (obj == app->btn[BUTTON_CONTENT]) + { + app->i_contnet++; + if(app->i_contnet>=sizeof(contents)/sizeof(char*)) + app->i_contnet=0; + evas_object_textblock_text_markup_set(app->txtblock,contents[app->i_contnet]); + } + else if (obj == app->btn[BUTTON_STYLE]) + { + app->i_style++; + if(app->i_style>=sizeof(styles)/sizeof(char*)) + app->i_style=0; + + Evas_Textblock_Style *style = evas_object_textblock_style_get(app->txtblock); + evas_textblock_style_set(style,styles[app->i_style]); + } + + elm_object_text_set(app->lbl_status, get_fit_status(app->txtblock)); +} + +char * get_fit_status(Eo * textblock) +{ + static char status[0xFFF]; + unsigned int options,min,max,step,size_array[256]; + size_t size_array_len; + evas_textblock_fit_options_get(textblock,&options); + evas_textblock_fit_size_range_get(textblock,&min,&max); + evas_textblock_fit_step_size_get(textblock,&step); + evas_textblock_fit_size_array_get(textblock,NULL,&size_array_len,0); + if (size_array_len>255) + size_array_len = 255; + evas_textblock_fit_size_array_get(textblock,size_array,NULL,size_array_len); + + strcpy(status,"Mode : "); + if (options == TEXTBLOCK_FIT_MODE_NONE) + strcat(status,"MODE_NONE"); + else if (options == TEXTBLOCK_FIT_MODE_HEIGHT) + strcat(status,"MODE_HEIGHT"); + else if (options == TEXTBLOCK_FIT_MODE_WIDTH) + strcat(status,"MODE_WIDTH"); + else if (options == TEXTBLOCK_FIT_MODE_ALL) + strcat(status,"MODE_ALL"); + + strcat(status,"
"); + sprintf(status + strlen(status),"Max : %d
",max); + sprintf(status + strlen(status),"Min : %d
",min); + sprintf(status + strlen(status),"Step : %d
",step); + sprintf(status + strlen(status),"Array : [ "); + for (size_t i = 0 ; i < 10 ; i++) + { + if(i"); + sprintf(status + strlen(status),"%s",styles_names[app->i_style]); + + + + return status; +} + +void +test_textblock_fit(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + app = calloc(sizeof(APP), 1); + + elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); + + app->win = elm_win_util_standard_add("Main", "App"); + elm_win_autodel_set(app->win, EINA_TRUE); + + app->box = elm_box_add(app->win); + app->boxHor = elm_box_add(app->box); + app->boxHor2 = elm_box_add(app->box); + app->txtblock = evas_object_textblock_add(app->box); + app->bg = elm_bg_add(app->box); + elm_bg_color_set(app->bg,255,255,255); + + Evas_Textblock_Style *style = evas_textblock_style_new(); + evas_textblock_style_set(style,styles[0]); + evas_object_textblock_style_set(app->txtblock,style); + evas_object_textblock_text_markup_set(app->txtblock,contents[0]); + + elm_box_horizontal_set(app->boxHor, EINA_TRUE); + elm_box_horizontal_set(app->boxHor2, EINA_TRUE); + + evas_object_size_hint_weight_set(app->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(app->box, EVAS_HINT_FILL, EVAS_HINT_FILL); + + + evas_object_size_hint_weight_set(app->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(app->box, EVAS_HINT_FILL, EVAS_HINT_FILL); + + evas_object_show(app->txtblock); + evas_object_show(app->bg); + evas_object_show(app->box); + evas_object_show(app->boxHor); + evas_object_show(app->boxHor2); + + elm_box_pack_end(app->box, app->bg); + elm_box_pack_end(app->box, app->boxHor); + elm_box_pack_end(app->box, app->boxHor2); + + elm_object_content_set(app->bg,app->txtblock); + + elm_win_resize_object_add(app->win, app->box); + evas_object_resize(app->win, 320, 480); + + for(int i = 0 ; i < BUTTON_ALL ; i++) + { + app->btn[i] = elm_button_add(app->boxHor); + evas_object_smart_callback_add(app->btn[i], "clicked", _btn_clicked, NULL); + elm_object_text_set(app->btn[i], BUTTON_STR[i]); + elm_box_pack_end(app->boxHor, app->btn[i]); + evas_object_show(app->btn[i]); + } + + app->lbl_status = elm_label_add(app->boxHor2); + elm_object_text_set(app->lbl_status, get_fit_status(app->txtblock)); + elm_box_pack_end(app->boxHor2, app->lbl_status); + evas_object_show(app->lbl_status); + + evas_object_size_hint_weight_set(app->txtblock, EVAS_HINT_EXPAND,EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(app->txtblock, EVAS_HINT_FILL, EVAS_HINT_FILL); + + evas_object_size_hint_weight_set(app->bg, EVAS_HINT_EXPAND,EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(app->bg, EVAS_HINT_FILL, EVAS_HINT_FILL); + + evas_object_show(app->win); +} + /*** Label Wrap **************************************************************/ void test_label_wrap(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) diff --git a/src/lib/edje/edje_data.c b/src/lib/edje/edje_data.c index 08fdae4256..ca9cbf11ad 100644 --- a/src/lib/edje/edje_data.c +++ b/src/lib/edje/edje_data.c @@ -1188,6 +1188,8 @@ _edje_edd_init(void) EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.size_range_max", text.size_range_max, EET_T_INT); EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.fit_x", text.fit_x, EET_T_UCHAR); EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.fit_y", text.fit_y, EET_T_UCHAR); + EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.fit_step", text.fit_step, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_LIST_UINT(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.fit_size_array", text.fit_size_array); EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.min_x", text.min_x, EET_T_UCHAR); EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.min_y", text.min_y, EET_T_UCHAR); EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.max_x", text.max_x, EET_T_UCHAR); diff --git a/src/lib/edje/edje_private.h b/src/lib/edje/edje_private.h index 33b07a2c26..c572d74991 100644 --- a/src/lib/edje/edje_private.h +++ b/src/lib/edje/edje_private.h @@ -1460,6 +1460,9 @@ struct _Edje_Part_Description_Spec_Text int id_text_source; /* -1 if none */ int size_range_min; int size_range_max; /* -1 means, no bound. */ + unsigned int fit_step; + /*FIXME THIS SHOULD BE EINA_LIST*/ + Eina_List *fit_size_array; unsigned char fit_x; /* resize font size down to fit in x dir */ unsigned char fit_y; /* resize font size down to fit in y dir */ diff --git a/src/lib/edje/edje_textblock.c b/src/lib/edje/edje_textblock.c index 76f3b2a8a3..d0c5c6d30d 100644 --- a/src/lib/edje/edje_textblock.c +++ b/src/lib/edje/edje_textblock.c @@ -1,25 +1,5 @@ #include "edje_private.h" -static double -_edje_part_recalc_single_textblock_scale_range_adjust(Edje_Part_Description_Text *chosen_desc, double base_scale, double scale) -{ - double size, min, max; - - if (chosen_desc->text.size == 0) - return scale; - - min = base_scale * chosen_desc->text.size_range_min; - max = chosen_desc->text.size_range_max * base_scale; - size = chosen_desc->text.size * scale; - - if ((size > max) && (max > 0)) - scale = max / (double)chosen_desc->text.size; - else if (size < min) - scale = min / (double)chosen_desc->text.size; - - return scale; -} - /* * Legacy function for min/max calculation of textblock part. * It can't calculate min/max properly in many cases. @@ -528,7 +508,6 @@ _edje_part_recalc_single_textblock(FLOAT_T sc, if (chosen_desc) { - Eina_Size2D size; if (ep->part->scale) evas_object_scale_set(ep->object, TO_DOUBLE(sc)); @@ -539,76 +518,28 @@ _edje_part_recalc_single_textblock(FLOAT_T sc, { if ((chosen_desc->text.fit_x) || (chosen_desc->text.fit_y)) { - double base_s = 1.0; - double orig_s; - double s = base_s; + unsigned int size_array[255]; + size_t size_array_len = 0; + Eina_List *l; + unsigned int *value; + EINA_LIST_FOREACH(chosen_desc->text.fit_size_array, l, value) + { + size_array[size_array_len++] = *value; + } + unsigned int mode = TEXTBLOCK_FIT_MODE_NONE; - if (ep->part->scale) base_s = TO_DOUBLE(sc); - efl_gfx_entity_scale_set(ep->object, base_s); - size = efl_canvas_textblock_size_native_get(ep->object); - - orig_s = base_s; - /* Now make it bigger so calculations will be more accurate - * and less influenced by hinting... */ - { - orig_s = _edje_part_recalc_single_textblock_scale_range_adjust(chosen_desc, base_s, - orig_s * TO_INT(params->eval.w) / size.w); - efl_gfx_entity_scale_set(ep->object, orig_s); - size = efl_canvas_textblock_size_native_get(ep->object); - } if (chosen_desc->text.fit_x) - { - if (size.w > 0) - { - s = _edje_part_recalc_single_textblock_scale_range_adjust(chosen_desc, base_s, - orig_s * TO_INT(params->eval.w) / size.w); - efl_gfx_entity_scale_set(ep->object, s); - efl_canvas_textblock_size_native_get(ep->object); - } - } + mode |= TEXTBLOCK_FIT_MODE_WIDTH; if (chosen_desc->text.fit_y) + mode |= TEXTBLOCK_FIT_MODE_HEIGHT; + evas_textblock_fit_options_set(ep->object, mode); + evas_textblock_fit_step_size_set(ep->object, chosen_desc->text.fit_step); + if ( chosen_desc->text.size_range_min || chosen_desc->text.size_range_max) + evas_textblock_fit_size_range_set(ep->object, chosen_desc->text.size_range_min, chosen_desc->text.size_range_max); + if (size_array_len>0) { - if (size.h > 0) - { - double tmp_s = _edje_part_recalc_single_textblock_scale_range_adjust(chosen_desc, base_s, - orig_s * TO_INT(params->eval.h) / size.h); - /* If we already have X fit, restrict Y to be no bigger - * than what we got with X. */ - if (!((chosen_desc->text.fit_x) && (tmp_s > s))) - { - s = tmp_s; - } - - efl_gfx_entity_scale_set(ep->object, s); - efl_canvas_textblock_size_native_get(ep->object); - } + evas_textblock_fit_size_array_set(ep->object,size_array,size_array_len); } - - /* Final tuning, try going down 90% at a time, hoping it'll - * actually end up being correct. */ - { - int i = 5; /* Tries before we give up. */ - Eina_Size2D size; - size = efl_canvas_textblock_size_native_get(ep->object); - - /* If we are still too big, try reducing the size to - * 95% each try. */ - while ((i > 0) && - ((chosen_desc->text.fit_x && (size.w > TO_INT(params->eval.w))) || - (chosen_desc->text.fit_y && (size.h > TO_INT(params->eval.h))))) - { - double tmp_s = _edje_part_recalc_single_textblock_scale_range_adjust(chosen_desc, base_s, s * 0.95); - - /* Break if we are not making any progress. */ - if (EQ(tmp_s, s)) - break; - s = tmp_s; - - efl_gfx_entity_scale_set(ep->object, s); - size = efl_canvas_textblock_size_native_get(ep->object); - i--; - } - } } if ((ed->file->efl_version.major >= 1) && (ed->file->efl_version.minor >= 19)) diff --git a/src/lib/eet/Eet.h b/src/lib/eet/Eet.h index 25babe1578..eb7cb5324f 100644 --- a/src/lib/eet/Eet.h +++ b/src/lib/eet/Eet.h @@ -3536,6 +3536,29 @@ eet_data_descriptor_encode(Eet_Data_Descriptor *edd, 0, /* 0, */ NULL, NULL); \ } while (0) +/** + * @ingroup Eet_Data_Group + * @brief Adds a linked list of unsigned integers to a data descriptor. + * @param edd The data descriptor to add the type to. + * @param struct_type The type of the struct. + * @param name The string name to use to encode/decode this member + * (must be a constant global and never change). + * @param member The struct member itself to be encoded. + * + * This macro lets you easily add a linked list of unsigned int. All the + * parameters are the same as for EET_DATA_DESCRIPTOR_ADD_BASIC(). + * + * @since 1.24.0 + */ +#define EET_DATA_DESCRIPTOR_ADD_LIST_UINT(edd, struct_type, name, member) \ + do { \ + struct_type ___ett; \ + eet_data_descriptor_element_add(edd, name, EET_T_UINT, EET_G_LIST, \ + (char *)(& (___ett.member)) - \ + (char *)(& (___ett)), \ + 0, /* 0, */ NULL, NULL); \ + } while (0) + /** * @ingroup Eet_Data_Group * @brief Adds a hash type to a data descriptor. diff --git a/src/lib/evas/canvas/evas_object_textblock.c b/src/lib/evas/canvas/evas_object_textblock.c index c09418d4be..019af5382d 100644 --- a/src/lib/evas/canvas/evas_object_textblock.c +++ b/src/lib/evas/canvas/evas_object_textblock.c @@ -139,6 +139,12 @@ static const char o_type[] = "textblock"; #define EINA_INLIST_REMOVE(l,i) do { l = (__typeof__(l)) eina_inlist_remove(EINA_INLIST_GET(l), EINA_INLIST_GET(i)); } while (0) #define EINA_INLIST_APPEND(l,i) do { l = (__typeof__(l)) eina_inlist_append(EINA_INLIST_GET(l), EINA_INLIST_GET(i)); } while (0) +/** + * @internal + * @typedef TEXT_FIT_CONTENT_CONFIG + * Configurations used to fit content inside Textblock + */ +typedef struct _TEXT_FIT_CONTENT_CONFIG TEXT_FIT_CONTENT_CONFIG; /** * @internal @@ -420,6 +426,20 @@ typedef struct _User_Style_Entry const char *key; } User_Style_Entry; +struct _TEXT_FIT_CONTENT_CONFIG +{ + unsigned int options; + unsigned int min_font_size,max_font_size; + unsigned int step_size; + unsigned int *p_size_array; + size_t size_list_length; + Eina_Size2D size_cache[256+1]; /** used hash font sizes 1-255 */ + Eina_Size2D last_size; + int last_size_index; + Eina_Bool force_refit; + char fit_style[256]; +}; + #define _FMT(x) (o->default_format.format.x) #define _FMT_INFO(x) (o->default_format.info.x) @@ -495,6 +515,7 @@ struct _Evas_Object_Textblock Eina_Hash *sources; Text_Item_Filter *text_items; // inlist } gfx_filter; + TEXT_FIT_CONTENT_CONFIG fit_content_config; Eina_Bool redraw : 1; Eina_Bool changed : 1; Eina_Bool pause_change : 1; @@ -509,6 +530,7 @@ struct _Evas_Object_Textblock Eina_Bool multiline : 1; Eina_Bool wrap_changed : 1; Eina_Bool auto_styles : 1; + Eina_Bool fit_in_progress : 1; }; struct _Evas_Textblock_Selection_Iterator @@ -619,6 +641,19 @@ static void _evas_textblock_cursor_copy(Efl_Text_Cursor_Handle *dst, const Efl_T static void _textblock_style_generic_set(Evas_Object *eo_obj, Evas_Textblock_Style *ts, const char *key); + +/*********Internal fitting Functions and Defines*********/ +int fit_cache_clear(TEXT_FIT_CONTENT_CONFIG *fc,const unsigned int fit_cache_flags); +int fit_text_block(Evas_Object *eo_obj); +int fit_fill_internal_list(TEXT_FIT_CONTENT_CONFIG *fc); +int fit_start_fitting(Evas_Object *eo_obj); +int fit_finish_fitting(Evas_Object *eo_obj); +Eina_Bool fit_is_fitting(const Evas_Object *eo_obj); +const unsigned int FIT_CACHE_CANVAS_SIZE = 0x0001; +const unsigned int FIT_CACHE_INTERNAL_SIZE_ARRAY = 0x0002; +const unsigned int FIT_CACHE_FORCE_REFIT = 0x0004; +const unsigned int FIT_CACHE_ALL = 0x000F; + /** selection iterator */ /** * @internal @@ -7369,6 +7404,11 @@ _layout_setup(Ctxt *c, const Eo *eo_obj, Evas_Coord w, Evas_Coord h) finalize = EINA_TRUE; } } + /* Extra Style used by fitting configure*/ + if (*o->fit_content_config.fit_style) + { + _format_fill(c->obj, c->fmt, o->fit_content_config.fit_style, EINA_FALSE); + } if (finalize) _format_finalize(c->obj, c->fmt); @@ -7463,7 +7503,9 @@ _relayout_if_needed(const Evas_Object *eo_obj, Efl_Canvas_Textblock_Data *o) if (obj->delete_me) return EINA_TRUE; /* XXX const */ - evas_object_textblock_coords_recalc((Evas_Object *)eo_obj, obj, obj->private_data); + if(!fit_is_fitting(eo_obj)) + evas_object_textblock_coords_recalc((Evas_Object *)eo_obj, obj, obj->private_data); + if (o->formatted.valid) { return EINA_TRUE; @@ -7621,6 +7663,10 @@ _efl_canvas_textblock_efl_object_constructor(Eo *eo_obj, Efl_Canvas_Textblock_Da _FMT(ellipsis) = -1; _FMT_INFO(bitmap_scalable) = EFL_TEXT_FONT_BITMAP_SCALABLE_COLOR; + /* Fit default properties*/ + evas_textblock_fit_size_range_set(eo_obj,1,255); + evas_textblock_fit_step_size_set(eo_obj,1); + o->auto_styles = EINA_TRUE; return eo_obj; @@ -11311,10 +11357,13 @@ _evas_textblock_changed(Efl_Canvas_Textblock_Data *o, Evas_Object *eo_obj) o->formatted.valid = 0; o->native.valid = 0; o->content_changed = 1; - if (o->markup_text) + if (!fit_is_fitting(eo_obj)) { - eina_stringshare_del(o->markup_text); - o->markup_text = NULL; + if (o->markup_text) + { + eina_stringshare_del(o->markup_text); + o->markup_text = NULL; + } } // FIXME: emit ONCE after this following checks @@ -11324,6 +11373,15 @@ _evas_textblock_changed(Efl_Canvas_Textblock_Data *o, Evas_Object *eo_obj) _cursor_emit_if_changed(data_obj); } + /* + If format changed we need to refit content again. + If content already fitting then ignore fitting (fitting cause fall to this callback) + */ + if (!fit_is_fitting(eo_obj)) + { + fit_cache_clear(&o->fit_content_config, FIT_CACHE_ALL); + fit_text_block(eo_obj); + } evas_object_change(eo_obj, obj); } @@ -14264,6 +14322,11 @@ evas_object_textblock_free(Evas_Object *eo_obj) /* remove obstacles */ _obstacles_free(eo_obj, o); + if (o->fit_content_config.p_size_array) + { + free(o->fit_content_config.p_size_array); + o->fit_content_config.p_size_array = NULL; + } #ifdef HAVE_HYPHEN /* Hyphenation */ @@ -15350,6 +15413,7 @@ _efl_canvas_textblock_efl_gfx_filter_filter_source_get(const Eo *obj EINA_UNUSED return eina_hash_find(pd->gfx_filter.sources, name); } + static void evas_object_textblock_coords_recalc(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, @@ -15407,6 +15471,17 @@ evas_object_textblock_coords_recalc(Evas_Object *eo_obj, o->formatted.valid = 0; o->changed = 1; } + + Evas_Coord x,y,w,h; + evas_object_geometry_get(eo_obj, &x, &y, &w, &h); + if ( + (w!=o->fit_content_config.last_size.w || h!=o->fit_content_config.last_size.h) && + (o->fit_content_config.options & TEXTBLOCK_FIT_MODE_ALL) != TEXTBLOCK_FIT_MODE_NONE + ) + { + fit_cache_clear(&o->fit_content_config, FIT_CACHE_INTERNAL_SIZE_ARRAY); + fit_text_block(eo_obj); + } } static void @@ -15534,6 +15609,54 @@ done: eo_obj, is_v, was_v); } +void fit_style_update(Evas_Object *object, int i_font_size, Eina_Bool disable_ellipsis, Eina_Bool disable_wrap) +{ + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(object, MY_CLASS); + TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config; + memset(fc->fit_style,0,sizeof(fc->fit_style)); + char * fit_style = fc->fit_style; + if (i_font_size >= 0) + { + char font_size[0xF] = {0}; + char *pfont = font_size; + sprintf(font_size, "font_size=%i ", i_font_size); + while (*pfont) + { + *fit_style = *pfont; + pfont++; + fit_style++; + } + } + + if (disable_ellipsis == EINA_TRUE) + { + *fit_style = ' '; + fit_style++; + char *p = "ellipsis=2.0"; + while (*p) + { + *fit_style = *p; + p++; + fit_style++; + } + } + + if (disable_wrap == EINA_TRUE) + { + *fit_style = ' '; + fit_style++; + char *p = "wrap=none"; + while (*p) + { + *fit_style = *p; + p++; + fit_style++; + } + } + + _canvas_text_format_changed(object,o); +} + static void evas_object_textblock_render_post(Evas_Object *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj, @@ -17026,6 +17149,371 @@ _efl_canvas_textblock_async_layout(Eo *eo_obj EINA_UNUSED, Efl_Canvas_Textblock_ NULL, ctx); return f; } +/* Fitting Internal Functions*/ + +int fit_cache_clear(TEXT_FIT_CONTENT_CONFIG *fc, unsigned int fit_cache_flags) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(fc, EVAS_ERROR_INVALID_PARAM); + if ((fit_cache_flags&FIT_CACHE_CANVAS_SIZE) == FIT_CACHE_CANVAS_SIZE) + fc->last_size = EINA_SIZE2D(0, 0); + if ((fit_cache_flags&FIT_CACHE_INTERNAL_SIZE_ARRAY) == FIT_CACHE_INTERNAL_SIZE_ARRAY) + for(int i = 0 ; i < 255 ; i++) fc->size_cache[i] = EINA_SIZE2D(0,0); + if ((fit_cache_flags&FIT_CACHE_FORCE_REFIT) == FIT_CACHE_FORCE_REFIT) + fc->force_refit = EINA_TRUE; + return EVAS_ERROR_SUCCESS; +} + +int fit_start_fitting(Evas_Object *eo_obj) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS); + if (o->fit_in_progress == EINA_TRUE) + return EVAS_ERROR_INVALID_OPERATION; + + o->fit_in_progress = EINA_TRUE; + return EVAS_ERROR_SUCCESS; +} + +int fit_finish_fitting(Evas_Object *eo_obj) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS); + if (o->fit_in_progress == EINA_FALSE) + return EVAS_ERROR_INVALID_OPERATION; + + o->fit_in_progress = EINA_FALSE; + return EVAS_ERROR_SUCCESS; +} + +Eina_Bool fit_is_fitting(const Evas_Object *eo_obj) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS); + return o->fit_in_progress; +} + +int fit_text_block(Evas_Object *eo_obj) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS); + Evas_Coord x,y,w,h; + Evas_Coord wf_new,hf_new; + + TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config; + + if (fc->options == TEXTBLOCK_FIT_MODE_NONE && !fc->force_refit) + return EVAS_ERROR_SUCCESS; + + + if (fc->options == TEXTBLOCK_FIT_MODE_NONE) + { + fit_start_fitting(eo_obj); + fc->force_refit = 0; + fit_style_update(eo_obj,-1, EINA_FALSE, EINA_FALSE); + fit_finish_fitting(eo_obj); + return EVAS_ERROR_SUCCESS; + } + + evas_object_geometry_get(eo_obj, &x, &y, &w, &h); + + if (w > 0 && h > 0) + { + Eina_Bool b_fit_width = ((fc->options & TEXTBLOCK_FIT_MODE_WIDTH) == TEXTBLOCK_FIT_MODE_WIDTH); + Eina_Bool b_fit_height = ((fc->options & TEXTBLOCK_FIT_MODE_HEIGHT) == TEXTBLOCK_FIT_MODE_HEIGHT); + //FIXME uncomment condition when style is not warp + if ( fc->force_refit || /*(w != fc->last_size.w && b_fit_width) || (h != fc->last_size.h && b_fit_height)*/ EINA_TRUE) + { + /* Extra Check to reduce recalculate */ + Eina_Bool b_max_reached = (fc->last_size_index == ((int)fc->size_list_length) - 1); + Eina_Bool b_min_reached = (fc->last_size_index == 0); + /* 1 - If max font size reached and text block size increased*/ + if (!fc->force_refit && b_max_reached && ((b_fit_width ? (w >= fc->last_size.w) : EINA_TRUE) && (b_fit_height ? (h >= fc->last_size.h) : EINA_TRUE))) + return EVAS_ERROR_SUCCESS; + /* 2- If min font size reached and text block size decreased*/ + if (!fc->force_refit && b_min_reached && ((b_fit_width ? (w <= fc->last_size.w) : EINA_TRUE) && (b_fit_height ? (h <= fc->last_size.h) : EINA_TRUE))) + { + /*This is needed to recalculate ellipsis, inside fitting to avoid losing markup_text*/ + fit_start_fitting(eo_obj); + _canvas_text_format_changed(eo_obj, o); + fit_finish_fitting(eo_obj); + return EVAS_ERROR_SUCCESS; + } + + fit_start_fitting(eo_obj); + + fc->force_refit = EINA_FALSE; + fc->last_size.w = w; + fc->last_size.h = h; + + int r = fc->size_list_length; + int l = 0; + + Eina_Bool bwrap = EINA_FALSE; + if (fc->options == TEXTBLOCK_FIT_MODE_WIDTH) + { + bwrap = EINA_TRUE; + } + + while(r > l) + { + int mid = (r + l) / 2; + /*cache font sizes vaules from 0-255 in size_cache array*/ + size_t font_size = fc->p_size_array[mid]; + if (font_size <= 0xFF && (fc->size_cache[font_size].w != 0 && fc->size_cache[font_size].h != 0)) + { + wf_new = fc->size_cache[font_size].w; + hf_new = fc->size_cache[font_size].h; + } + else + { + fit_style_update(eo_obj,fc->p_size_array[mid],EINA_TRUE,bwrap); + Eina_Size2D size = efl_canvas_textblock_size_formatted_get(eo_obj); + wf_new = size.w; + hf_new = size.h; + if (fc->p_size_array[mid]<255) + { + fc->size_cache[font_size].w = wf_new; + fc->size_cache[font_size].h = hf_new; + } + } + + if ( + ((wf_new > w) & ((fc->options & TEXTBLOCK_FIT_MODE_WIDTH) == TEXTBLOCK_FIT_MODE_WIDTH)) || + ((hf_new > h) & ((fc->options & TEXTBLOCK_FIT_MODE_HEIGHT) == TEXTBLOCK_FIT_MODE_HEIGHT))) + { + r = mid; + } + else + { + l = mid + 1; + } + } + + /*Lower bound founded, subtract one to move for nearest value*/ + fc->last_size_index = MAX(l-1, 0); + fit_style_update(eo_obj,fc->p_size_array[fc->last_size_index],(fc->last_size_index != 0) && fc->options != TEXTBLOCK_FIT_MODE_HEIGHT ,EINA_FALSE); + fit_finish_fitting(eo_obj); + } + } + return EVAS_ERROR_SUCCESS; +} + +int fit_fill_internal_list(TEXT_FIT_CONTENT_CONFIG *fc) +{ + int diff = (fc->max_font_size - fc->min_font_size); + if (fc->p_size_array) + { + free(fc->p_size_array); + fc->p_size_array = NULL; + } + if (diff == 0) + { + fc->size_list_length = 1; + fc->p_size_array = malloc(sizeof(unsigned int) * fc->size_list_length); + if (!fc->p_size_array) + return EVAS_ERROR_NO_MEMORY; + fc->p_size_array[0] = fc->max_font_size; + return EVAS_ERROR_SUCCESS; + } + + fc->size_list_length = 2 + diff / MAX(fc->step_size, 1); + fc->p_size_array = malloc(sizeof(unsigned int) * fc->size_list_length); + if (!fc->p_size_array) + return EVAS_ERROR_NO_MEMORY; + + size_t i ; + for (i = 0 ; i < fc->size_list_length - 1; i++) + { + fc->p_size_array[i] = fc->min_font_size + i * MAX(fc->step_size, 1); + } + fc->p_size_array[fc->size_list_length - 1] = fc->max_font_size; + fc->last_size_index = -1; + return EVAS_ERROR_SUCCESS; +} + + + +EAPI int evas_textblock_fit_options_set(Evas_Object *obj, unsigned int options) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS); + TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config; + if (fc->options == options) + return EVAS_ERROR_SUCCESS; + + fc->options = options; + fit_cache_clear(fc, FIT_CACHE_ALL); + fit_text_block(obj); + return EVAS_ERROR_SUCCESS; +} + +EAPI int evas_textblock_fit_options_get(const Evas_Object *obj, unsigned int *p_options) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS); + TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config; + if (p_options) + *p_options = fc->options; + return EVAS_ERROR_SUCCESS; +} + +EAPI int evas_textblock_fit_size_range_set(Evas_Object *obj, unsigned int min_font_size, unsigned int max_font_size) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS); + TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config; + + Eina_Bool max_changed = fc->max_font_size != max_font_size; + Eina_Bool min_changed = fc->min_font_size != min_font_size; + + /* last_selected_size used for optimization calculations + * If last_size_index already recach last element in p_size_array + * Skip optimization by setting last_selected_size to -1 + */ + int last_selected_size = fc->last_size_index; + if (last_selected_size == ((int)fc->size_list_length-1)) + last_selected_size = -1; + + if (!max_changed && !min_changed) + return EVAS_ERROR_SUCCESS; + + if (min_font_size < 0 || max_font_size <0) + return EVAS_ERROR_INVALID_PARAM; + + if (max_font_size < min_font_size) + return EVAS_ERROR_INVALID_PARAM; + + fc->max_font_size = max_font_size; + fc->min_font_size = min_font_size; + + int n_ret = EVAS_ERROR_SUCCESS; + n_ret = fit_cache_clear(fc,FIT_CACHE_FORCE_REFIT); + if (n_ret) return n_ret; + n_ret = fit_fill_internal_list(fc); + if (n_ret) return n_ret; + + /* Optimization to reduce calculations + * If only max size changed and last fit size index is still valid, then no need to recalculation + * Where changing max font size will not change content of p_size_array for sizes < max_size + */ + if (min_changed || (last_selected_size == -1 || last_selected_size > ((int)fc->size_list_length-1))) + { + n_ret = fit_text_block(obj); + if (n_ret) return n_ret; + } + else + { + /* Keep fit size index */ + fc->last_size_index = last_selected_size; + } + return EVAS_ERROR_SUCCESS; +} + +EAPI int evas_textblock_fit_size_range_get(const Evas_Object *obj, unsigned int *p_min_font_size, unsigned int *p_max_font_size) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS); + TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config; + + if (p_min_font_size) + *p_min_font_size = fc->min_font_size; + + if (p_max_font_size) + *p_max_font_size = fc->max_font_size; + + return EVAS_ERROR_SUCCESS; +} + +EAPI int evas_textblock_fit_step_size_set(Evas_Object *obj, unsigned int step_size) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS); + TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config; + if (fc->step_size == step_size) + return EVAS_ERROR_SUCCESS; + + if (step_size == 0) + return EVAS_ERROR_INVALID_PARAM; + + fc->step_size = step_size; + int n_ret = EVAS_ERROR_SUCCESS; + n_ret = fit_cache_clear(fc, FIT_CACHE_FORCE_REFIT); + if (n_ret) return n_ret; + n_ret = fit_fill_internal_list(fc); + if (n_ret) return n_ret; + n_ret = fit_text_block(obj); + if (n_ret) return n_ret; + return EVAS_ERROR_SUCCESS; +} + +EAPI int evas_textblock_fit_step_size_get(const Evas_Object *obj, unsigned int * p_step_size) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS); + TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config; + if (p_step_size) + *p_step_size = fc->step_size; + return EVAS_ERROR_SUCCESS; +} + +int compareUINT(const void * a, const void * b) +{ + unsigned int a_value = *(const unsigned int*)a; + unsigned int b_value = *(const unsigned int*)b; + + if(a_value > b_value) return 1; + else if(a_value < b_value) return -1; + else return 0; +} + +EAPI int evas_textblock_fit_size_array_set(Evas_Object *obj, const unsigned int *p_size_array, size_t size_array_len) +{ + int n_ret = EVAS_ERROR_SUCCESS; + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS); + TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config; + if (size_array_len == 0) + return EVAS_ERROR_INVALID_PARAM; + + if (fc->p_size_array) + { + free(fc->p_size_array); + fc->p_size_array = NULL; + fc->size_list_length = 0; + } + + fc->p_size_array = malloc(sizeof(unsigned int) * size_array_len); + if (!fc->p_size_array) return EVAS_ERROR_NO_MEMORY; + memcpy(fc->p_size_array,p_size_array,sizeof(unsigned int) * size_array_len); + fc->size_list_length = size_array_len; + + fc->last_size_index = -1; + + qsort(fc->p_size_array,fc->size_list_length,sizeof(unsigned int),compareUINT); + + n_ret = fit_cache_clear(fc, FIT_CACHE_FORCE_REFIT); + if (n_ret) return n_ret; + n_ret = fit_text_block(obj); + if (n_ret) return n_ret; + return EVAS_ERROR_SUCCESS; +} + +EAPI int evas_textblock_fit_size_array_get(const Evas_Object *obj, unsigned int *p_size_array, size_t *p_size_array_len, size_t passed_array_size) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM); + Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS); + TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config; + if (p_size_array) + { + size_t num = MIN(passed_array_size,fc->size_list_length); + memcpy(p_size_array,fc->p_size_array,sizeof(unsigned int)* num); + } + if (p_size_array_len) + { + *p_size_array_len = fc->size_list_length; + } + return EVAS_ERROR_SUCCESS; +} #include "canvas/efl_canvas_textblock.eo.c" #include "canvas/efl_canvas_textblock_eo.legacy.c" diff --git a/src/lib/evas/canvas/evas_textblock_legacy.h b/src/lib/evas/canvas/evas_textblock_legacy.h index f3299112e1..b852c0d61b 100644 --- a/src/lib/evas/canvas/evas_textblock_legacy.h +++ b/src/lib/evas/canvas/evas_textblock_legacy.h @@ -1056,6 +1056,106 @@ EAPI void evas_textblock_cursor_char_delete(Evas_Textblock_Cursor *cur); * @ingroup Evas_Textblock */ EAPI Evas_Textblock_Cursor *evas_object_textblock_cursor_get(const Evas_Object *obj); + + + +/* TEXT BLOCK FIT OPTIONS FLAGS*/ +#define TEXTBLOCK_FIT_MODE_NONE 0x0000 +#define TEXTBLOCK_FIT_MODE_WIDTH 0x0001 +#define TEXTBLOCK_FIT_MODE_HEIGHT 0x0002 +#define TEXTBLOCK_FIT_MODE_ALL 0x0003 + +/* TEXT BLOCK ERROR CODES*/ +/* FIXME this hould go to other public place*/ +#define EVAS_ERROR_SUCCESS 0x0000 +#define EVAS_ERROR_INVALID_PARAM 0x0001 +#define EVAS_ERROR_NO_MEMORY 0x0002 +#define EVAS_ERROR_INVALID_OPERATION 0x0003 + + +/** Get the object's content it options. + * + * @param obj The textblock object. + * @param[out] options content fitting options. + * @return Returns error code. + */ +EAPI int evas_textblock_fit_options_get(const Evas_Object *obj, unsigned int * p_options); + +/** Set the object's content it options. + * + * @param obj The textblock object. + * @param[in] options content fitting options. + * @return Returns error code. + */ +EAPI int evas_textblock_fit_options_set(Evas_Object *obj, unsigned int options); + +/** Get the object's max and min font sizes used for fitting content. + * + * @param obj The textblock object. + * @param[out] p_min_font_size min font size used when fitting content. + * @param[out] p_max_font_size max font size used when fitting content. + * @return Returns error code. + */ +EAPI int evas_textblock_fit_size_range_get(const Evas_Object *obj, unsigned int *p_min_font_size, unsigned int *p_max_font_size); + +/** Set the object's max and min font sizes used for fitting content. + * + * @param obj The textblock object. + * @param[in] min_font_size min font size used when fitting content. + * @param[in] max_font_size max font size used when fitting content. + * @return Returns error code. + */ +EAPI int evas_textblock_fit_size_range_set(Evas_Object *obj, unsigned int min_font_size, unsigned int max_font_size); + + +/** Get the object's fitting step size when trying fonts between min font size and + * max font size. + * + * @param obj The textblock object. + * @param[out] p_step_size step jumps between min and max font size. + * @return Returns error code. + */ +EAPI int evas_textblock_fit_step_size_get(const Evas_Object *obj, unsigned int *p_step_size); + + +/** Set the object's fitting step size when trying fonts between min font size and + * max font size. + * + * @param obj The textblock object. + * @param[out] step_size step jumps between min and max font size. + * @return Returns error code. + */ +EAPI int evas_textblock_fit_step_size_set(Evas_Object *obj, unsigned int step_size); + +/** Get copy of the object's fitting font size array used internally + * + * @param obj The textblock object. + * @param[out] p_size_array pointer to size array (passing NULL will ignore filling array). + * @param[out] p_size_array_len the length of internall font sizes array. + * @param[out] request_size_array request to fill specific amount in p_size_array. + * @return Returns error code. + */ +EAPI int evas_textblock_fit_size_array_get(const Evas_Object *obj, unsigned int *p_size_array, size_t *p_size_array_len,size_t request_size_array); + +/** Set the object's fitting font size array that will be used internally + * Changing fitting step_size,min_font_size,max_font size will generate new array + * Internall array will be sorted + * + * @param obj The textblock object. + * @param[in] p_size_array pointer to font sizes array. + * @param[in] size_array_len the length passed font sizes array. + * @return Returns error code. + */ +EAPI int evas_textblock_fit_size_array_set(Evas_Object *obj, const unsigned int *p_size_array, size_t size_array_len); + + + + + + + + + #include "canvas/efl_canvas_textblock_eo.legacy.h" /** * @} diff --git a/src/tests/evas/evas_test_textblock.c b/src/tests/evas/evas_test_textblock.c index 786be4a3f0..a450ebc608 100644 --- a/src/tests/evas/evas_test_textblock.c +++ b/src/tests/evas/evas_test_textblock.c @@ -4134,6 +4134,32 @@ EFL_START_TEST(evas_textblock_obstacle) } EFL_END_TEST; +EFL_START_TEST(evas_textblock_fit) +{ + START_TB_TEST(); + Evas_Coord fw, fh,fw_new, fh_new; + int n_ret; + const char *buf = + "This is an example text to demonstrate the textblock object" + " with content fit feature."; + evas_object_textblock_text_markup_set(tb, buf); + evas_object_resize(tb, 300, 300); + evas_object_textblock_size_formatted_get(tb, &fw, &fh); + n_ret = evas_textblock_fit_options_set(tb,TEXTBLOCK_FIT_MODE_ALL); + fail_if(n_ret != EVAS_ERROR_SUCCESS); + n_ret = evas_textblock_fit_size_range_set(tb,1,50); + fail_if(n_ret != EVAS_ERROR_SUCCESS); + evas_object_textblock_size_formatted_get(tb, &fw_new, &fh_new); + fail_if(fw_new == fw && fh_new == fh); + unsigned int size_array[3] = {150,200,250}; + n_ret = evas_textblock_fit_size_array_set(tb,size_array,3); + fail_if(n_ret != EVAS_ERROR_SUCCESS); + evas_object_textblock_size_formatted_get(tb, &fw, &fh); + fail_if(fw_new == fw && fh_new == fh); + END_TB_TEST(); +} +EFL_END_TEST; + #ifdef HAVE_HYPHEN static void _hyphenation_width_stress(Evas_Object *tb, Evas_Textblock_Cursor *cur) @@ -4654,6 +4680,7 @@ void evas_test_textblock(TCase *tc) tcase_add_test(tc, evas_textblock_items); tcase_add_test(tc, evas_textblock_delete); tcase_add_test(tc, evas_textblock_obstacle); + tcase_add_test(tc, evas_textblock_fit); #ifdef HAVE_HYPHEN tcase_add_test(tc, evas_textblock_hyphenation); #endif