diff --git a/data/elementary/images/Makefile.am b/data/elementary/images/Makefile.am index 6ecc178d52..6f028257cb 100644 --- a/data/elementary/images/Makefile.am +++ b/data/elementary/images/Makefile.am @@ -50,7 +50,8 @@ elementary_images_files = \ elementary/images/pm_fill.png \ elementary/images/pt.png \ elementary/images/earth_normal.png \ - elementary/images/space.png + elementary/images/space.png \ + elementary/images/image_items.eet elementary_images_glayer_files = \ elementary/images/g_layer/double_tap_1.png \ diff --git a/data/elementary/images/image_items.eet b/data/elementary/images/image_items.eet new file mode 100644 index 0000000000..1db45dfaa3 Binary files /dev/null and b/data/elementary/images/image_items.eet differ diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am index b080c61f87..86925ce943 100644 --- a/src/Makefile_Elementary.am +++ b/src/Makefile_Elementary.am @@ -45,6 +45,9 @@ elm_public_eolian_files = \ lib/elementary/efl_ui_popup_anchor.eo \ lib/elementary/efl_ui_text_editable.eo \ lib/elementary/efl_ui_text_async.eo \ + lib/elementary/efl_ui_text_factory_images.eo \ + lib/elementary/efl_ui_text_factory_emoticons.eo \ + lib/elementary/efl_ui_text_factory_fallback.eo \ lib/elementary/efl_ui_textpath.eo \ lib/elementary/efl_ui_translatable.eo \ lib/elementary/efl_ui_clock.eo \ @@ -738,6 +741,9 @@ lib_elementary_libelementary_la_SOURCES = \ lib/elementary/efl_ui_table_static.c \ lib/elementary/efl_ui_table_private.h \ lib/elementary/efl_ui_text.c \ + lib/elementary/efl_ui_text_factory_images.c \ + lib/elementary/efl_ui_text_factory_emoticons.c \ + lib/elementary/efl_ui_text_factory_fallback.c \ lib/elementary/efl_ui_clock.c \ lib/elementary/efl_ui_clock_private.h \ lib/elementary/efl_ui_image_factory.c \ diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index 36298162a2..c26a978943 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -7,6 +7,7 @@ evas_canvas_eolian_pub_files = \ lib/evas/canvas/efl_canvas_polygon.eo \ lib/evas/canvas/efl_canvas_rectangle.eo \ lib/evas/canvas/efl_canvas_text.eo \ + lib/evas/canvas/efl_canvas_text_factory.eo \ lib/evas/canvas/efl_canvas_group.eo \ lib/evas/canvas/efl_canvas_image_internal.eo \ lib/evas/canvas/evas_canvas3d_camera.eo\ diff --git a/src/bin/elementary/test.c b/src/bin/elementary/test.c index 1f75b930cb..0f7fdacfe3 100644 --- a/src/bin/elementary/test.c +++ b/src/bin/elementary/test.c @@ -333,6 +333,7 @@ void test_code_diff_inline(void *data, Evas_Object *obj, void *event_info); void test_efl_ui_text(void *data, Evas_Object *obj, void *event_info); void test_efl_ui_text_label(void *data, Evas_Object *obj, void *event_info); void test_efl_ui_text_async(void *data, Evas_Object *obj, void *event_info); +void test_ui_text_item_factory(void *data, Evas_Object *obj, void *event_info); void test_evas_mask(void *data, Edje_Object *obj, void *event_info); void test_gfx_filters(void *data, Evas_Object *obj, void *event_info); void test_evas_snapshot(void *data, Evas_Object *obj, void *event_info); @@ -834,6 +835,7 @@ add_tests: ADD_TEST_EO(NULL, "Entries", "Efl.Ui.Text", test_efl_ui_text); ADD_TEST_EO(NULL, "Entries", "Efl.Ui.Text Label", test_efl_ui_text_label); ADD_TEST_EO(NULL, "Entries", "Efl.Ui.Text.Async", test_efl_ui_text_async); + ADD_TEST_EO(NULL, "Entries", "Ui.Text Item Factory", test_ui_text_item_factory); //------------------------------// ADD_TEST(NULL, "Advanced Entries", "Code Syntax", test_code_syntax); diff --git a/src/bin/elementary/test_efl_ui_text.c b/src/bin/elementary/test_efl_ui_text.c index b53c427b9a..e28ba3bc64 100644 --- a/src/bin/elementary/test_efl_ui_text.c +++ b/src/bin/elementary/test_efl_ui_text.c @@ -144,8 +144,8 @@ static void my_efl_ui_text_bt_4(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Evas_Object *en = data; - efl_text_cursor_object_item_insert(en, efl_text_cursor_get(en, EFL_TEXT_CURSOR_GET_MAIN), - "size=32x32 href=emoticon/evil-laugh"); + efl_text_cursor_item_insert(en, efl_text_cursor_get(en, EFL_TEXT_CURSOR_GET_MAIN), + "emoticon/evil-laugh", "size=32x32"); } static void @@ -187,6 +187,7 @@ test_efl_ui_text(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *eve { Evas_Object *win, *bx, *bx2, *bx3, *bt, *en; Efl_Text_Cursor_Cursor *main_cur, *cur; + char buf[128]; win = elm_win_util_standard_add("entry", "Entry"); elm_win_autodel_set(win, EINA_TRUE); @@ -215,9 +216,10 @@ test_efl_ui_text(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *eve cur = efl_text_cursor_new(en); efl_text_cursor_position_set(en, cur, 2); - efl_text_cursor_object_item_insert(en, cur, "size=32x32 href=emoticon"); + efl_text_cursor_item_insert(en, cur, "emoticon/happy", "size=32x32"); efl_text_cursor_position_set(en, cur, 50); - efl_text_cursor_object_item_insert(en, cur, "size=32x32 href=emoticon"); + sprintf(buf, "file://%s/images/sky_01.jpg", elm_app_data_dir_get()); + efl_text_cursor_item_insert(en, cur, buf, "size=32x32"); efl_text_cursor_position_set(en, main_cur, 5); efl_text_cursor_position_set(en, cur, 20); @@ -297,7 +299,7 @@ test_efl_ui_text_async(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, voi elm_win_resize_object_add(win, bx); evas_object_show(bx); - en = efl_add(EFL_UI_TEXT_ASYNC_CLASS, win, + en = efl_add(EFL_UI_TEXT_CLASS, win, efl_text_wrap_set(efl_added, EFL_TEXT_FORMAT_WRAP_WORD), efl_text_multiline_set(efl_added, EINA_TRUE) ); @@ -332,3 +334,196 @@ test_efl_ui_text_async(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, voi evas_object_resize(win, 480, 320); evas_object_show(win); } + +#define IMAGES_SZ 5 + +static const char *images[IMAGES_SZ] = { + "sky", "logo", "dog", "eet_rock", "eet_plant" }; + +static void +my_efl_ui_text_item_factory_bt_image(void *data, Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + Evas_Object *en = data; + static int image_idx = 0; + + image_idx = (image_idx + 1) % IMAGES_SZ; + efl_text_cursor_item_insert(en, + efl_text_cursor_get(en, EFL_TEXT_CURSOR_GET_MAIN), + images[image_idx], "size=32x32"); + printf("Inserted image: key = %s\n", images[image_idx]); +} + +static void +my_efl_ui_text_item_factory_bt_emoticon(void *data, Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + Evas_Object *en = data; + efl_text_cursor_item_insert(en, efl_text_cursor_get(en, EFL_TEXT_CURSOR_GET_MAIN), + "emoticon/evil-laugh", "size=32x32"); +} + +static struct +{ + const char *name; + Eo *item_factory; +} factories[3]; + +static void +my_efl_ui_text_item_factory_bt_change(void *data, Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + Evas_Object *en = data; + static int item_factory_idx = 0; + + item_factory_idx = (item_factory_idx + 1) % 3; + efl_ui_text_item_factory_set(en, factories[item_factory_idx].item_factory); + printf("Factory set to: %s\n", factories[item_factory_idx].name); +} + +#define FACTORY_NONE 0 +#define FACTORY_IMAGE 1 +#define FACTORY_EMOTICON 2 + +static void +_ui_text_factory_del(void *data, const Efl_Event *ev EINA_UNUSED) +{ + Eina_File *f = data; + eina_file_close(f); + efl_del(factories[FACTORY_IMAGE].item_factory); + efl_del(factories[FACTORY_EMOTICON].item_factory); +} + +void +test_ui_text_item_factory(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Evas_Object *win, *bx, *bx2, *bx3, *bt, *en; + Efl_Text_Cursor_Cursor *main_cur, *cur; + char buf[128]; + Eina_File *f; + + win = elm_win_util_standard_add("entry", "Entry"); + elm_win_autodel_set(win, EINA_TRUE); + + bx = elm_box_add(win); + evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, bx); + evas_object_show(bx); + + en = efl_add(EFL_UI_TEXT_CLASS, win, + efl_text_multiline_set(efl_added, EINA_TRUE)); + + factories[FACTORY_NONE].name = "None (Fallback)"; + factories[FACTORY_NONE].item_factory = NULL; + + factories[FACTORY_IMAGE].name = "Image Factory"; + factories[FACTORY_IMAGE].item_factory = + efl_add(EFL_UI_TEXT_FACTORY_IMAGES_CLASS, win); + + factories[FACTORY_EMOTICON].name = "Emoticon Factory"; + factories[FACTORY_EMOTICON].item_factory = + efl_add(EFL_UI_TEXT_FACTORY_EMOTICONS_CLASS, win); + + // Test assigning file path source + sprintf(buf, "%s/images/sky_01.jpg", elm_app_data_dir_get()); + efl_ui_text_factory_images_matches_add(factories[FACTORY_IMAGE].item_factory, + images[0], buf, NULL); + sprintf(buf, "%s/images/logo.png", elm_app_data_dir_get()); + efl_ui_text_factory_images_matches_add(factories[FACTORY_IMAGE].item_factory, + images[1], buf, NULL); + sprintf(buf, "%s/images/mystrale.jpg", elm_app_data_dir_get()); + efl_ui_text_factory_images_matches_add(factories[FACTORY_IMAGE].item_factory, + images[2], buf, NULL); + + // Open EET source w/ key + sprintf(buf, "%s/images/image_items.eet", elm_app_data_dir_get()); + f = eina_file_open(buf, EINA_FALSE); + if (f) + { + efl_event_callback_add(en, EFL_EVENT_DEL, _ui_text_factory_del, f); + + efl_ui_text_factory_images_matches_mmap_add( + factories[FACTORY_IMAGE].item_factory, + "eet_rock", f, "rock"); + efl_ui_text_factory_images_matches_mmap_add( + factories[FACTORY_IMAGE].item_factory, + "eet_plant", f, "plant"); + } + else + { + printf("Error loading test file. Please review test."); + } + + + printf("Added Efl.Ui.Text object\n"); + efl_text_set(en, "Hello world! Goodbye world! This is a test text for the" + " new UI Text widget.\xE2\x80\xA9This is the next paragraph.\nThis" + " is the next line.\nThis is Yet another line! Line and paragraph" + " separators are actually different!"); + efl_text_font_set(en, "Sans", 14); + efl_text_normal_color_set(en, 255, 255, 255, 255); + + main_cur = efl_text_cursor_get(en, EFL_TEXT_CURSOR_GET_MAIN); + cur = efl_text_cursor_new(en); + + efl_text_cursor_position_set(en, cur, 2); + efl_text_cursor_item_insert(en, cur, "emoticon/happy", "size=32x32"); + efl_text_cursor_position_set(en, cur, 50); + + sprintf(buf, "file://%s/images/sky_01.jpg", elm_app_data_dir_get()); + efl_text_cursor_item_insert(en, cur, buf, "size=32x32"); + efl_text_cursor_position_set(en, main_cur, 5); + + efl_ui_text_interactive_editable_set(en, EINA_TRUE); + efl_ui_text_scrollable_set(en, EINA_TRUE); + elm_box_pack_end(bx, en); + elm_object_focus_set(en, EINA_TRUE); + + bx2 = elm_box_add(win); + elm_box_horizontal_set(bx2, EINA_TRUE); + evas_object_size_hint_weight_set(bx2, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(bx2, EVAS_HINT_FILL, EVAS_HINT_FILL); + + bt = elm_button_add(win); + elm_object_text_set(bt, "Image"); + evas_object_smart_callback_add(bt, "clicked", + my_efl_ui_text_item_factory_bt_image, en); + evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, 0.0); + elm_box_pack_end(bx2, bt); + elm_object_focus_allow_set(bt, EINA_FALSE); + evas_object_show(bt); + + bt = elm_button_add(win); + elm_object_text_set(bt, "Emoticon"); + evas_object_smart_callback_add(bt, "clicked", + my_efl_ui_text_item_factory_bt_emoticon, en); + evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, 0.0); + elm_box_pack_end(bx2, bt); + elm_object_focus_allow_set(bt, EINA_FALSE); + evas_object_show(bt); + + bt = elm_button_add(win); + elm_object_text_set(bt, "Factory"); + evas_object_smart_callback_add(bt, "clicked", + my_efl_ui_text_item_factory_bt_change, en); + evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, 0.0); + elm_box_pack_end(bx2, bt); + elm_object_focus_allow_set(bt, EINA_FALSE); + evas_object_show(bt); + + bx3 = elm_box_add(win); + elm_box_horizontal_set(bx3, EINA_TRUE); + evas_object_size_hint_weight_set(bx3, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(bx3, EVAS_HINT_FILL, EVAS_HINT_FILL); + + elm_box_pack_end(bx, bx3); + elm_box_pack_end(bx, bx2); + evas_object_show(bx3); + evas_object_show(bx2); + + evas_object_resize(win, 480, 320); + evas_object_show(win); +} diff --git a/src/lib/efl/interfaces/efl_text_annotate.eo b/src/lib/efl/interfaces/efl_text_annotate.eo index 002505356c..7826d07991 100644 --- a/src/lib/efl/interfaces/efl_text_annotate.eo +++ b/src/lib/efl/interfaces/efl_text_annotate.eo @@ -76,23 +76,6 @@ interface Efl.Text.Annotate { } return: bool; [[$true on success, $false otherwise.]] } - object_item_geometry_get { - [[Queries a given object item for its geometry. - - Note that the provided annotation should be an object item type. - - @since 1.18 - ]] - legacy: null; - params { - @in an: ptr(const(Efl.Text.Annotate.Annotation)); [[Given annotation to query]] - @out x: int; [[X coordinate of the annotation]] - @out y: int; [[Y coordinate of the annotation]] - @out w: int; [[Width of the annotation]] - @out h: int; [[Height of the annotation]] - } - return: bool; [[$true if given annotation is an object item, $false otherwise]] - } annotation_positions_get { [[Sets given cursors to the start and end positions of the annotation. @@ -111,8 +94,41 @@ interface Efl.Text.Annotate { position of the annotation in the text]] } } + annotation_is_item { + [[Whether this is an "item" type of annotation. Should be used before + querying the annotation's geometry, as only "item" annotations have + a geometry. + + see @.cursor_item_insert + see @.item_geometry_get + + @since 1.21 + ]] + legacy: null; + params { + annotation: ptr(Efl.Text.Annotate.Annotation); [[Given annotation]] + } + return: bool; [[$true if given annotation is an object item, $false otherwise]] + } + item_geometry_get { + [[Queries a given object item for its geometry. + + Note that the provided annotation should be an object item type. + + @since 1.18 + ]] + legacy: null; + params { + @in an: ptr(const(Efl.Text.Annotate.Annotation)); [[Given annotation to query]] + @out x: int; [[X coordinate of the annotation]] + @out y: int; [[Y coordinate of the annotation]] + @out w: int; [[Width of the annotation]] + @out h: int; [[Height of the annotation]] + } + return: bool; [[$true if given annotation is an object item, $false otherwise]] + } // Cursor - @property cursor_object_item_annotation { + @property cursor_item_annotation { [[The object-item annotation at the cursor's position.]] get { legacy: null; @@ -124,7 +140,7 @@ interface Efl.Text.Annotate { cur: ptr(Efl.Text.Cursor.Cursor); [[Cursor object]] } } - cursor_object_item_insert { + cursor_item_insert { [[Inserts a object item at specified position. This adds a placeholder to be queried by higher-level code, @@ -134,8 +150,11 @@ interface Efl.Text.Annotate { legacy: null; params { cur: ptr(Efl.Text.Cursor.Cursor); [[Cursor object]] - @in format: string; [[Format of the inserted item. - See Format styles.]] + @in item: string; [[Item key to be used in higher-up + code to query and decided what image, emoticon + etc. to embed.]] + @in format: string; [[Size format of the inserted item. + This hints how to size the item in the text.]] } return: ptr(Efl.Text.Annotate.Annotation); [[The annotation handle of the inserted item.]] diff --git a/src/lib/elementary/Elementary.h b/src/lib/elementary/Elementary.h index a16e3f8a0f..0880126512 100644 --- a/src/lib/elementary/Elementary.h +++ b/src/lib/elementary/Elementary.h @@ -311,6 +311,9 @@ typedef Eo Efl_Ui_Focus_Manager; # include # include # include +# include +# include +# include # include # include # include diff --git a/src/lib/elementary/efl_ui_text.c b/src/lib/elementary/efl_ui_text.c index ed4f5d1718..ae74e5c42a 100644 --- a/src/lib/elementary/efl_ui_text.c +++ b/src/lib/elementary/efl_ui_text.c @@ -63,7 +63,8 @@ struct _Efl_Ui_Text_Data Eina_List *sel; Eina_List *items; /** context menu item list */ Item_Obj *item_objs; - Eina_List *item_providers; + Efl_Canvas_Text_Factory *item_factory; + Efl_Canvas_Text_Factory *item_fallback_factory; Eina_List *markup_filters; Ecore_Job *hov_deljob; Mod_Api *api; // module api if supplied @@ -131,6 +132,7 @@ struct _Efl_Ui_Text_Data Eina_Bool scroll : 1; Eina_Bool input_panel_show_on_demand : 1; Eina_Bool anchors_updated : 1; + Eina_Bool fallback_item_provider_disabled : 1; }; struct _Anchor @@ -2485,43 +2487,22 @@ _entry_mouse_triple_signal_cb(void *data, static Evas_Object * _item_get(void *data, const char *item) { - Eina_List *l; - Evas_Object *o; - Elm_Entry_Item_Provider *ip; - const char *style = elm_widget_style_get(data); + Evas_Object *o = NULL; EFL_UI_TEXT_DATA_GET(data, sd); - EINA_LIST_FOREACH(sd->item_providers, l, ip) + if (item) { - o = ip->func(ip->data, data, item); - if (o) return o; - } - if (item && !strncmp(item, "file://", 7)) - { - const char *fname = item + 7; - - o = evas_object_image_filled_add(evas_object_evas_get(data)); - evas_object_image_file_set(o, fname, NULL); - if (evas_object_image_load_error_get(o) == EVAS_LOAD_ERROR_NONE) + if (sd->item_factory) { - evas_object_show(o); + o = efl_canvas_text_factory_create(sd->item_factory, data, item); } - else + else if (sd->item_fallback_factory) { - evas_object_del(o); - o = edje_object_add(evas_object_evas_get(data)); - elm_widget_theme_object_set - (data, o, "text/emoticon", "wtf", style); + o = efl_canvas_text_factory_create(sd->item_fallback_factory, + data, item); } - return o; } - - o = edje_object_add(evas_object_evas_get(data)); - if (!elm_widget_theme_object_set - (data, o, "text", item, style)) - elm_widget_theme_object_set - (data, o, "text/emoticon", "wtf", style); return o; } @@ -3215,6 +3196,7 @@ _efl_ui_text_efl_object_constructor(Eo *obj, Efl_Ui_Text_Data *sd) if (_elm_config->desktop_entry) sd->sel_handler_disabled = EINA_TRUE; + sd->item_fallback_factory = efl_add(EFL_UI_TEXT_FACTORY_FALLBACK_CLASS, obj); _create_text_cursors(obj, sd); return obj; @@ -3224,7 +3206,6 @@ EOLIAN static void _efl_ui_text_efl_object_destructor(Eo *obj, Efl_Ui_Text_Data *sd) { Elm_Entry_Context_Menu_Item *it; - Elm_Entry_Item_Provider *ip; Elm_Entry_Markup_Filter *tf; Eo *text_obj; @@ -3270,10 +3251,6 @@ _efl_ui_text_efl_object_destructor(Eo *obj, Efl_Ui_Text_Data *sd) eina_stringshare_del(it->icon_group); free(it); } - EINA_LIST_FREE(sd->item_providers, ip) - { - free(ip); - } EINA_LIST_FREE(sd->markup_filters, tf) { _filter_free(tf); @@ -3314,6 +3291,9 @@ _efl_ui_text_efl_object_destructor(Eo *obj, Efl_Ui_Text_Data *sd) ecore_job_del(sd->deferred_decoration_job); sd->deferred_decoration_job = NULL; + if (sd->item_factory) efl_unref(sd->item_factory); + if (sd->item_fallback_factory) efl_del(sd->item_fallback_factory); + efl_destructor(efl_super(obj, MY_CLASS)); } @@ -3539,55 +3519,6 @@ _efl_ui_text_context_menu_disabled_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd return !sd->context_menu; } -EOLIAN static void -_efl_ui_text_item_provider_append(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Elm_Entry_Item_Provider_Cb func, void *data) -{ - Elm_Entry_Item_Provider *ip; - - EINA_SAFETY_ON_NULL_RETURN(func); - - ip = calloc(1, sizeof(Elm_Entry_Item_Provider)); - if (!ip) return; - - ip->func = func; - ip->data = data; - sd->item_providers = eina_list_append(sd->item_providers, ip); -} - -EOLIAN static void -_efl_ui_text_item_provider_prepend(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Elm_Entry_Item_Provider_Cb func, void *data) -{ - Elm_Entry_Item_Provider *ip; - - EINA_SAFETY_ON_NULL_RETURN(func); - - ip = calloc(1, sizeof(Elm_Entry_Item_Provider)); - if (!ip) return; - - ip->func = func; - ip->data = data; - sd->item_providers = eina_list_prepend(sd->item_providers, ip); -} - -EOLIAN static void -_efl_ui_text_item_provider_remove(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Elm_Entry_Item_Provider_Cb func, void *data) -{ - Eina_List *l; - Elm_Entry_Item_Provider *ip; - - EINA_SAFETY_ON_NULL_RETURN(func); - - EINA_LIST_FOREACH(sd->item_providers, l, ip) - { - if ((ip->func == func) && ((!data) || (ip->data == data))) - { - sd->item_providers = eina_list_remove_list(sd->item_providers, l); - free(ip); - return; - } - } -} - EOLIAN static Eina_Bool _efl_ui_text_efl_file_file_set(Eo *obj, Efl_Ui_Text_Data *sd, const char *file, const char *group EINA_UNUSED) { @@ -4927,8 +4858,7 @@ _anchors_create(Eo *obj, Efl_Ui_Text_Data *sd) Eina_Bool is_anchor = EINA_FALSE; Eina_Bool is_item = EINA_FALSE; - if (efl_text_object_item_geometry_get(obj, anchor, - NULL, NULL, NULL, NULL)) + if (efl_text_annotation_is_item(obj, anchor)) { is_anchor = EINA_TRUE; is_item = EINA_TRUE; @@ -5052,7 +4982,7 @@ _anchors_update(Eo *o, Efl_Ui_Text_Data *sd) { rect->obj = ob; - efl_text_object_item_geometry_get(an->obj, + efl_text_item_geometry_get(an->obj, an->annotation, &cx, &cy, &cw, &ch); evas_object_move(rect->obj, x + cx, y + cy); evas_object_resize(rect->obj, cw, ch); @@ -5296,6 +5226,20 @@ _efl_ui_text_move_cb(void *data, Evas *e EINA_UNUSED, _decoration_defer_all(data); } +static void +_efl_ui_text_item_factory_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *pd, + Efl_Canvas_Text_Factory *item_factory) +{ + if (pd->item_factory) efl_unref(pd->item_factory); + pd->item_factory = efl_ref(item_factory); +} + +static Eo * +_efl_ui_text_item_factory_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *pd) +{ + return pd->item_factory; +} + #if 0 /* Efl.Part begin */ diff --git a/src/lib/elementary/efl_ui_text.eo b/src/lib/elementary/efl_ui_text.eo index 341a951679..515fc0dfda 100644 --- a/src/lib/elementary/efl_ui_text.eo +++ b/src/lib/elementary/efl_ui_text.eo @@ -233,13 +233,12 @@ class Efl.Ui.Text (Efl.Ui.Layout, Elm.Interface_Scrollable, Efl.Ui.Clickable, enabled: bool; [[If $enabled is $true, the return key is automatically disabled when the entry has no text.]] } } - item_provider_prepend { - [[This prepends a custom item provider to the list for that entry - - This prepends the given callback.]] - params { - @in func: Elm_Entry_Item_Provider_Cb; [[The function called to provide the item object.]] - @in data: void_ptr @optional; [[The data passed to $func.]] + @property item_factory { + [[The factory that provides item in the text e.g. + "emoticon/happy" or "href=file://image.jpg" etc. + ]] + values { + item_factory: Efl.Canvas.Text.Factory; [[Factory to create items]] } } input_panel_show { @@ -252,17 +251,6 @@ class Efl.Ui.Text (Efl.Ui.Layout, Elm.Interface_Scrollable, Efl.Ui.Clickable, selection_copy { [[This executes a "copy" action on the selected text in the entry.]] } - item_provider_remove { - [[This removes a custom item provider to the list for that entry - - This removes the given callback. See @.item_provider_append for - more information - ]] - params { - @in func: Elm_Entry_Item_Provider_Cb; [[The function called to provide the item object.]] - @in data: void_ptr @optional; [[The data passed to $func.]] - } - } context_menu_clear { [[This clears and frees the items in a entry's contextual (longpress) menu. @@ -306,23 +294,6 @@ class Efl.Ui.Text (Efl.Ui.Layout, Elm.Interface_Scrollable, Efl.Ui.Clickable, selection_cut { [[This executes a "cut" action on the selected text in the entry.]] } - item_provider_append { - [[This appends a custom item provider to the list for that entry - - This appends the given callback. The list is walked from beginning to end - with each function called given the item href string in the text. If the - function returns an object handle other than $null (it should create an - object to do this), then this object is used to replace that item. If - not the next provider is called until one provides an item object, or the - default provider in entry does. - - See also \@ref entry-items. - ]] - params { - @in func: Elm_Entry_Item_Provider_Cb; [[The function called to provide the item object.]] - @in data: void_ptr @optional; [[The data passed to $func.]] - } - } context_menu_item_add { [[This adds an item to the entry's contextual menu. diff --git a/src/lib/elementary/efl_ui_text_factory_emoticons.c b/src/lib/elementary/efl_ui_text_factory_emoticons.c new file mode 100644 index 0000000000..55818af33d --- /dev/null +++ b/src/lib/elementary/efl_ui_text_factory_emoticons.c @@ -0,0 +1,52 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include +#include +#include "elm_priv.h" + +#define MY_CLASS EFL_UI_TEXT_FACTORY_EMOTICONS_CLASS + +typedef struct _Efl_Ui_Text_Factory_Emoticons_Data Efl_Ui_Text_Factory_Emoticons_Data; + +struct _Efl_Ui_Text_Factory_Emoticons_Data +{ + const char *name; +}; + +EOLIAN static Eo * +_efl_ui_text_factory_emoticons_efl_object_constructor(Eo *obj, + Efl_Ui_Text_Factory_Emoticons_Data *pd EINA_UNUSED) +{ + obj = efl_constructor(efl_super(obj, MY_CLASS)); + return obj; +} + +EOLIAN static void +_efl_ui_text_factory_emoticons_efl_object_destructor(Eo *obj, + Efl_Ui_Text_Factory_Emoticons_Data *pd EINA_UNUSED) +{ + efl_destructor(efl_super(obj, MY_CLASS)); +} + + +EOLIAN static Efl_Canvas_Object +*_efl_ui_text_factory_emoticons_efl_canvas_text_factory_create( + Eo *obj EINA_UNUSED, + Efl_Ui_Text_Factory_Emoticons_Data *pd EINA_UNUSED, + Efl_Canvas_Object *object, + const char *key) +{ + Eo *o; + const char *style = elm_widget_style_get(object); + + o = edje_object_add(evas_object_evas_get(object)); + if (!_elm_theme_object_set + (object, o, "text", key, style)) + _elm_theme_object_set + (object, o, "text/emoticon", "wtf", style); + return o; +} + +#include "efl_ui_text_factory_emoticons.eo.c" diff --git a/src/lib/elementary/efl_ui_text_factory_emoticons.eo b/src/lib/elementary/efl_ui_text_factory_emoticons.eo new file mode 100644 index 0000000000..38269ea7d5 --- /dev/null +++ b/src/lib/elementary/efl_ui_text_factory_emoticons.eo @@ -0,0 +1,12 @@ +class Efl.Ui.Text.Factory.Emoticons (Efl.Object, Efl.Canvas.Text.Factory) +{ + [[Factory that creates emoticons from the current theme given a key. + + @since 1.21 + ]] + implements { + Efl.Object.constructor; + Efl.Object.destructor; + Efl.Canvas.Text.Factory.create; + } +} diff --git a/src/lib/elementary/efl_ui_text_factory_fallback.c b/src/lib/elementary/efl_ui_text_factory_fallback.c new file mode 100644 index 0000000000..4ee456e1e0 --- /dev/null +++ b/src/lib/elementary/efl_ui_text_factory_fallback.c @@ -0,0 +1,62 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include +#include +#include "elm_priv.h" + +#define MY_CLASS EFL_UI_TEXT_FACTORY_FALLBACK_CLASS + +typedef struct _Efl_Ui_Text_Factory_Fallback_Data Efl_Ui_Text_Factory_Fallback_Data; + +struct _Efl_Ui_Text_Factory_Fallback_Data +{ + Efl_Canvas_Text_Factory *emoticon_factory, *image_factory; +}; + +EOLIAN static Eo * +_efl_ui_text_factory_fallback_efl_object_constructor(Eo *obj, + Efl_Ui_Text_Factory_Fallback_Data *pd EINA_UNUSED) +{ + obj = efl_constructor(efl_super(obj, MY_CLASS)); + pd->image_factory = efl_add(EFL_UI_TEXT_FACTORY_IMAGES_CLASS, obj); + pd->emoticon_factory = efl_add(EFL_UI_TEXT_FACTORY_EMOTICONS_CLASS, obj); + return obj; +} + +EOLIAN static void +_efl_ui_text_factory_fallback_efl_object_destructor(Eo *obj, + Efl_Ui_Text_Factory_Fallback_Data *pd EINA_UNUSED) +{ + efl_del(pd->emoticon_factory); + efl_del(pd->image_factory); + efl_destructor(efl_super(obj, MY_CLASS)); +} + + +EOLIAN static Efl_Canvas_Object +*_efl_ui_text_factory_fallback_efl_canvas_text_factory_create( + Eo *obj EINA_UNUSED, + Efl_Ui_Text_Factory_Fallback_Data *pd EINA_UNUSED, + Efl_Canvas_Object *object, + const char *key) +{ + Efl_Canvas_Object *o = NULL; + + // Parse the string. Can be either: + // 1. some/name - an emoticon (load from theme) + // 2. file:// - image file + if (key && !strncmp(key, "file://", 7)) + { + const char *fname = key + 7; + o = efl_canvas_text_factory_create(pd->image_factory, object, fname); + } + else + { + o = efl_canvas_text_factory_create(pd->emoticon_factory, object, key); + } + return o; +} + +#include "efl_ui_text_factory_fallback.eo.c" diff --git a/src/lib/elementary/efl_ui_text_factory_fallback.eo b/src/lib/elementary/efl_ui_text_factory_fallback.eo new file mode 100644 index 0000000000..830fa5b637 --- /dev/null +++ b/src/lib/elementary/efl_ui_text_factory_fallback.eo @@ -0,0 +1,16 @@ +class Efl.Ui.Text.Factory.Fallback (Efl.Object, Efl.Canvas.Text.Factory) +{ + [[Internal factory for fallback cases. + + This wraps some internal functionality: + - Contains 2 factories: image and emoticon + - Strips off "file://" prefix for image items, to be used with the image + factory. + @since 1.21 + ]] + implements { + Efl.Object.constructor; + Efl.Object.destructor; + Efl.Canvas.Text.Factory.create; + } +} diff --git a/src/lib/elementary/efl_ui_text_factory_images.c b/src/lib/elementary/efl_ui_text_factory_images.c new file mode 100644 index 0000000000..91325f15b2 --- /dev/null +++ b/src/lib/elementary/efl_ui_text_factory_images.c @@ -0,0 +1,147 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include +#include +#include "elm_priv.h" + +#define MY_CLASS EFL_UI_TEXT_FACTORY_IMAGES_CLASS + +typedef struct _Efl_Ui_Text_Factory_Images_Data Efl_Ui_Text_Factory_Images_Data; + +struct _Efl_Ui_Text_Factory_Images_Data +{ + const char *name; + Eina_Hash *hash; +}; + +typedef struct +{ + Eina_File *file; + const char *key; +} File_Entry; + +static void +_entry_free_cb(void *data) +{ + File_Entry *e = data; + eina_file_close(e->file); + eina_stringshare_del(e->key); + free(e); +} + +EOLIAN static Eo * +_efl_ui_text_factory_images_efl_object_constructor(Eo *obj, + Efl_Ui_Text_Factory_Images_Data *pd EINA_UNUSED) +{ + obj = efl_constructor(efl_super(obj, MY_CLASS)); + pd->hash = eina_hash_string_superfast_new(_entry_free_cb); + return obj; +} + +EOLIAN static void +_efl_ui_text_factory_images_efl_object_destructor(Eo *obj, + Efl_Ui_Text_Factory_Images_Data *pd EINA_UNUSED) +{ + eina_hash_free(pd->hash); + efl_destructor(efl_super(obj, MY_CLASS)); +} + +EOLIAN static Efl_Canvas_Object * +_efl_ui_text_factory_images_efl_canvas_text_factory_create(Eo *obj EINA_UNUSED, + Efl_Ui_Text_Factory_Images_Data *pd EINA_UNUSED, + Efl_Canvas_Object *object, + const char *key) +{ + Efl_Canvas_Object *o; + File_Entry *e; + + o = efl_add(EFL_CANVAS_IMAGE_CLASS, object); + e = eina_hash_find(pd->hash, key); + if (e) + { + efl_file_mmap_set(o, e->file, e->key); + } + else + { + efl_file_set(o, key, NULL); + } + + if (efl_file_load_error_get(o) != EFL_IMAGE_LOAD_ERROR_NONE) + { + efl_del(o); + o = NULL; + } + + return o; +} + +EOLIAN static Eina_Bool +_efl_ui_text_factory_images_matches_add(Eo *obj EINA_UNUSED, + Efl_Ui_Text_Factory_Images_Data *pd, + const char *name, const char *file, const char *key) +{ + File_Entry *e; + Eina_File *f = eina_file_open(file, EINA_FALSE); + + if (!f) return EINA_FALSE; + + e = malloc(sizeof(*e)); + e->file = f; + e->key = eina_stringshare_add(key); + + if (!eina_hash_add(pd->hash, name, e)) + { + ERR("Failed to add file path %s to key %s\n", file, key); + eina_file_close(f); + free(e); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_text_factory_images_matches_del(Eo *obj EINA_UNUSED, + Efl_Ui_Text_Factory_Images_Data *pd, + const char *name) +{ + return eina_hash_del(pd->hash, name, NULL); +} + +EOLIAN static Eina_Bool +_efl_ui_text_factory_images_matches_mmap_add(Eo *obj EINA_UNUSED, + Efl_Ui_Text_Factory_Images_Data *pd, + const char *name, const Eina_File *file, const char *key) +{ + File_Entry *e; + Eina_File *f; + + if (!file) return EINA_FALSE; + + f = eina_file_dup(file); + e = malloc(sizeof(*e)); + e->file = f; + e->key = eina_stringshare_add(key); + + if (!eina_hash_add(pd->hash, name, e)) + { + ERR("Failed to add Eina_File %p to key %s\n", file, key); + eina_file_close(f); + free(e); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_text_factory_images_matches_mmap_del(Eo *obj EINA_UNUSED, + Efl_Ui_Text_Factory_Images_Data *pd, + const char *name) +{ + return eina_hash_del(pd->hash, name, NULL); +} + +#include "efl_ui_text_factory_images.eo.c" diff --git a/src/lib/elementary/efl_ui_text_factory_images.eo b/src/lib/elementary/efl_ui_text_factory_images.eo new file mode 100644 index 0000000000..d682e294a7 --- /dev/null +++ b/src/lib/elementary/efl_ui_text_factory_images.eo @@ -0,0 +1,69 @@ +class Efl.Ui.Text.Factory.Images (Efl.Object, Efl.Canvas.Text.Factory) +{ + [[Factory that creates images given key string + + The key can be either a full image path, or associated with one. The + factory will fallback if key was not matches with an image, and try + to load it as a full path. + + @since 1.21 + ]] + methods { + matches_add { + [[Associates given name with a path of an image or EET file. + + This can be used for quick retrieval (instead of + providing actual filenames. + + This $file is associated with $name is considered a full file path. + + see @.matches_mmap_add for mmap version + see @.matches_del + ]] + params { + name: string; [[the name associated with filename]] + path: string; [[the image or EET file path]] + key: string; [[the key to use (in cases of loading an EET file]] + } + return: bool; [[$true if successful, $false otherwise]] + } + matches_del { + [[Deletes an association of $key with its respective file path. + + see @.matches_add + ]] + params { + key: string; [[the entry's key to delete]] + } + return: bool; [[$true if successful, $false otherwise]] + } + matches_mmap_add { + [[Associates given name with a mmap'd image or EET file and key. + + see @.matches_add for string file path version + see @.matches_mmap_del + ]] + params { + name: string; [[the name associated with filename]] + file: ptr(const(Eina.File)); [[the image or EET file]] + key: string; [[the key to use (in cases of loading an EET file]] + } + return: bool; [[$true if successful, $false otherwise]] + } + matches_mmap_del { + [[Deletes an association of $key with its respective file. + + see @.matches_mmap_add + ]] + params { + key: string; [[the entry's key to delete]] + } + return: bool; [[$true if successful, $false otherwise]] + } + } + implements { + Efl.Object.constructor; + Efl.Object.destructor; + Efl.Canvas.Text.Factory.create; + } +} diff --git a/src/lib/evas/Evas_Eo.h b/src/lib/evas/Evas_Eo.h index d6664a2ddd..f2b2f6d372 100644 --- a/src/lib/evas/Evas_Eo.h +++ b/src/lib/evas/Evas_Eo.h @@ -21,6 +21,7 @@ */ //#include "canvas/efl_canvas_text_cursor.eo.h" #include "canvas/efl_canvas_text.eo.h" +#include "canvas/efl_canvas_text_factory.eo.h" /** * @} */ diff --git a/src/lib/evas/canvas/efl_canvas_text.eo b/src/lib/evas/canvas/efl_canvas_text.eo index a021fbacef..73367b0f58 100644 --- a/src/lib/evas/canvas/efl_canvas_text.eo +++ b/src/lib/evas/canvas/efl_canvas_text.eo @@ -345,10 +345,11 @@ class Efl.Canvas.Text (Efl.Canvas.Object, Efl.Text, Efl.Text.Properties, Efl.Text.Annotate.range_annotations_get; Efl.Text.Annotate.annotation_insert; Efl.Text.Annotate.annotation_del; - Efl.Text.Annotate.object_item_geometry_get; + Efl.Text.Annotate.annotation_is_item; + Efl.Text.Annotate.item_geometry_get; Efl.Text.Annotate.annotation_positions_get; - Efl.Text.Annotate.cursor_object_item_annotation { get; } - Efl.Text.Annotate.cursor_object_item_insert; + Efl.Text.Annotate.cursor_item_annotation { get; } + Efl.Text.Annotate.cursor_item_insert; Efl.Text.Markup.markup { set; get; } Efl.Text.Markup.cursor_markup_insert; Efl.Gfx.scale { set; } diff --git a/src/lib/evas/canvas/efl_canvas_text_factory.eo b/src/lib/evas/canvas/efl_canvas_text_factory.eo new file mode 100644 index 0000000000..240cfb4669 --- /dev/null +++ b/src/lib/evas/canvas/efl_canvas_text_factory.eo @@ -0,0 +1,25 @@ +interface Efl.Canvas.Text.Factory () +{ + [[Object factory that creates Efl.Canvas.Object objects. + + Translates a given key to an object (item), to be later placed in a text + for higher level usages. The translation implementation is left to be + decided by the inheriting class, whether it is by treating the $key as an + image path, or a key associated with a real-path in a hashtable + or something else entirely. + + @since 1.21 + ]] + methods { + create { + [[Translates a given $key to an item object, and returns the object. + The returned object should be owned by the passed $object. + ]] + params { + object: Efl.Canvas.Object; [[The parent of the created object]] + key: string; [[Key that is associated to an item object]] + } + return: Efl.Canvas.Object @owned; + } + } +} diff --git a/src/lib/evas/canvas/evas_object_textblock.c b/src/lib/evas/canvas/evas_object_textblock.c index 70c5618390..1a0023da5f 100644 --- a/src/lib/evas/canvas/evas_object_textblock.c +++ b/src/lib/evas/canvas/evas_object_textblock.c @@ -15123,16 +15123,24 @@ _efl_canvas_text_efl_text_annotate_range_annotations_get(Eo *eo_obj EINA_UNUSED, } EOLIAN static Efl_Text_Annotate_Annotation * -_efl_canvas_text_efl_text_annotate_cursor_object_item_insert(Eo *eo_obj, Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur, const char *format) +_efl_canvas_text_efl_text_annotate_cursor_item_insert(Eo *eo_obj, + Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur, + const char *item, const char *format) { + Eina_Strbuf *buf = eina_strbuf_new(); + + eina_strbuf_append_printf(buf, "%s href=%s", format, item); + Efl_Text_Annotate_Annotation *ret = - _textblock_annotation_insert(cur->obj, o, cur, cur, format, EINA_TRUE); + _textblock_annotation_insert(cur->obj, o, cur, cur, + eina_strbuf_string_get(buf), EINA_TRUE); + eina_strbuf_free(buf); efl_event_callback_legacy_call(eo_obj, EFL_CANVAS_TEXT_EVENT_CHANGED, NULL); return ret; } EOLIAN static Efl_Text_Annotate_Annotation * -_efl_canvas_text_efl_text_annotate_cursor_object_item_annotation_get(Eo *eo_obj EINA_UNUSED, +_efl_canvas_text_efl_text_annotate_cursor_item_annotation_get(Eo *eo_obj EINA_UNUSED, Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur) { Eina_Iterator *it; @@ -15153,7 +15161,21 @@ _efl_canvas_text_efl_text_annotate_cursor_object_item_annotation_get(Eo *eo_obj } EOLIAN static Eina_Bool -_efl_canvas_text_efl_text_annotate_object_item_geometry_get(Eo *eo_obj, Efl_Canvas_Text_Data *o EINA_UNUSED, +_efl_canvas_text_efl_text_annotate_annotation_is_item(Eo *eo_obj EINA_UNUSED, + Efl_Canvas_Text_Data *o EINA_UNUSED, + Efl_Text_Annotate_Annotation *annotation) +{ + if (!annotation || (annotation->obj != eo_obj)) + { + ERR("Used invalid handle or of a different object"); + return EINA_FALSE; + } + + return annotation->is_item; +} + +EOLIAN static Eina_Bool +_efl_canvas_text_efl_text_annotate_item_geometry_get(Eo *eo_obj, Efl_Canvas_Text_Data *o EINA_UNUSED, const Efl_Text_Annotate_Annotation *an, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch) { Efl_Text_Cursor_Cursor cur; @@ -16264,3 +16286,4 @@ _efl_canvas_text_async_layout(Eo *eo_obj EINA_UNUSED, Efl_Canvas_Text_Data *o) } #include "canvas/efl_canvas_text.eo.c" +#include "canvas/efl_canvas_text_factory.eo.c" // interface diff --git a/src/tests/evas/evas_test_textblock.c b/src/tests/evas/evas_test_textblock.c index 0e6057cf58..14d04a7e10 100644 --- a/src/tests/evas/evas_test_textblock.c +++ b/src/tests/evas/evas_test_textblock.c @@ -4371,8 +4371,8 @@ START_TEST(evas_textblock_annotation) /* Check "item" annotations */ efl_text_set(tb, "abcd"); evas_textblock_cursor_pos_set(cur, 4); - an = efl_text_cursor_object_item_insert(tb, cur, "size=16x16"); - _test_check_annotation(tb, 4, 4, _COMP_PARAMS("size=16x16")); + an = efl_text_cursor_item_insert(tb, cur, "", "size=16x16"); + _test_check_annotation(tb, 4, 4, _COMP_PARAMS("size=16x16 href=")); /* Check that format is not extended if it's an "object item" */ evas_textblock_cursor_pos_set(cur, 5); @@ -4417,14 +4417,14 @@ START_TEST(evas_textblock_annotation) /* Test getting of object item */ evas_textblock_cursor_pos_set(cur, 4); - an = efl_text_cursor_object_item_annotation_get(tb, cur); + an = efl_text_cursor_item_annotation_get(tb, cur); ck_assert(!an); - an = efl_text_cursor_object_item_insert(tb, cur, "size=16x16"); + an = efl_text_cursor_item_insert(tb, cur, "", "size=16x16"); evas_textblock_cursor_pos_set(cur, 4); - an = efl_text_cursor_object_item_annotation_get(tb, cur); + an = efl_text_cursor_item_annotation_get(tb, cur); ck_assert(an); - ck_assert_str_eq("size=16x16", efl_text_annotation_get(tb, an)); + ck_assert_str_eq("size=16x16 href=", efl_text_annotation_get(tb, an)); END_TB_TEST(); }