From aef19e9017c46d312f9c9c15b4f2bf0355e0961f Mon Sep 17 00:00:00 2001 From: Marcel Hollerbach Date: Wed, 27 Feb 2019 13:29:08 -0500 Subject: [PATCH] efl_ui_widget: introduce a new API Summary: this new API can be used to get a easy to use iterator, to iterate through all the children of a specific widget. ref T7553 Reviewers: stefan_schmidt, zmike, cedric, segfaultxavi Reviewed By: segfaultxavi Subscribers: woohyun, cedric, #reviewers, #committers Tags: #efl Maniphest Tasks: T7553 Differential Revision: https://phab.enlightenment.org/D8014 --- src/Makefile_Elementary.am | 3 + src/lib/elementary/Efl_Ui.h | 2 + src/lib/elementary/efl_ui_widget_common.c | 173 ++++++++++++++++++++ src/lib/elementary/efl_ui_widget_common.h | 35 +++++ src/lib/elementary/meson.build | 1 + src/tests/elementary/efl_ui_suite.c | 1 + src/tests/elementary/efl_ui_suite.h | 1 + src/tests/elementary/efl_ui_test_widget.c | 182 ++++++++++++++++++++++ src/tests/elementary/meson.build | 1 + 9 files changed, 399 insertions(+) create mode 100644 src/lib/elementary/efl_ui_widget_common.c create mode 100644 src/lib/elementary/efl_ui_widget_common.h create mode 100644 src/tests/elementary/efl_ui_test_widget.c diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am index 43a3d64edb..c020b343d4 100644 --- a/src/Makefile_Elementary.am +++ b/src/Makefile_Elementary.am @@ -347,6 +347,7 @@ includesunstable_HEADERS = \ lib/elementary/elm_interface_scrollable.h \ lib/elementary/elm_interfaces.h \ lib/elementary/elm_widget.h \ + lib/elementary/efl_ui_widget_common.h \ lib/elementary/elm_widget_actionslider.h \ lib/elementary/elm_widget_box.h \ lib/elementary/elm_widget_bubble.h \ @@ -816,6 +817,7 @@ lib_elementary_libelementary_la_SOURCES = \ lib/elementary/elm_view_form.c \ lib/elementary/elm_web2.c \ lib/elementary/efl_ui_widget.c \ + lib/elementary/efl_ui_widget_common.c \ lib/elementary/efl_ui_win.c \ lib/elementary/efl_ui_win_inlined.c \ lib/elementary/efl_ui_win_socket.c \ @@ -1630,6 +1632,7 @@ tests_elementary_efl_ui_suite_SOURCES = \ tests/elementary/efl_ui_test_image.c \ tests/elementary/efl_ui_test_image_zoomable.c \ tests/elementary/efl_ui_test_layout.c \ + tests/elementary/efl_ui_test_widget.c \ tests/elementary/efl_ui_suite.h \ tests/elementary/efl_ui_model.c diff --git a/src/lib/elementary/Efl_Ui.h b/src/lib/elementary/Efl_Ui.h index 33cc92efcb..bca10e3455 100644 --- a/src/lib/elementary/Efl_Ui.h +++ b/src/lib/elementary/Efl_Ui.h @@ -170,6 +170,8 @@ EAPI void efl_ui_focus_relation_free(Efl_Ui_Focus_Relations *rel); # include # include +# include +# include # include # include # include diff --git a/src/lib/elementary/efl_ui_widget_common.c b/src/lib/elementary/efl_ui_widget_common.c new file mode 100644 index 0000000000..020848887b --- /dev/null +++ b/src/lib/elementary/efl_ui_widget_common.c @@ -0,0 +1,173 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#define EFL_ACCESS_OBJECT_PROTECTED +#define EFL_ACCESS_COMPONENT_PROTECTED +#define ELM_WIDGET_PROTECTED +#define ELM_WIDGET_ITEM_PROTECTED +#define EFL_INPUT_EVENT_PROTECTED +#define EFL_UI_L10N_PROTECTED +#define EFL_UI_FOCUS_OBJECT_PROTECTED +#define EFL_UI_WIDGET_PART_BG_PROTECTED +#define EFL_PART_PROTECTED + +#include + +#include "elm_priv.h" +#include "elm_widget_container.h" +#include "elm_interface_scrollable.h" +#include "elm_part_helper.h" +#include "elm_widget_combobox.h" + +typedef struct { + Eina_Iterator iterator; + Efl_Ui_Widget *origin; //where we started + Efl_Ui_Widget *current; // the current widget where the iterator is +} Widget_Iterator; + +static Widget_Iterator* +iter_init(Efl_Ui_Widget *origin) +{ + Widget_Iterator *it; + + it = calloc(1, sizeof(Widget_Iterator)); + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->origin = origin; + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(NULL); + it->iterator.free = FUNC_ITERATOR_FREE(free); + + return it; +} + +static Efl_Ui_Widget* +_fetch_parent_widget(Efl_Gfx_Entity* o) +{ + Efl_Ui_Widget *parent; + + if (efl_isa(o, EFL_UI_WIDGET_CLASS)) + parent = efl_ui_widget_parent_get(o); + else + parent = evas_object_data_get(o, "elm-parent"); + + return parent; +} + +static Efl_Ui_Widget* +_next_widget(Efl_Gfx_Entity* o) +{ + Efl_Ui_Widget *parent; + Eina_List *rel; + + parent = _fetch_parent_widget(o); + ELM_WIDGET_DATA_GET_OR_RETURN(parent, pd, NULL); + rel = eina_list_data_find_list(pd->subobjs, o); + + return eina_list_data_get(eina_list_next(rel)); +} + +static Eina_Bool +_widget_next(Widget_Iterator *it, void **data) +{ + Efl_Ui_Widget *runner; + Efl_Ui_Widget_Data *pd = NULL; + + //Init case + if (!it->current) + { + it->current = it->origin; + goto deliver; + } + + if (efl_isa(it->current, EFL_UI_WIDGET_CLASS)) + { + pd = efl_data_scope_safe_get(it->current, EFL_UI_WIDGET_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINA_FALSE); + } + + //If there is a child, go there + if (pd && pd->subobjs) + { + it->current = eina_list_data_get(pd->subobjs); + goto deliver; + } + + //If there is no child, then iterate up the parents until we find a widget with a next widget + runner = it->current; + do + { + Efl_Ui_Widget *tmp = _next_widget(runner); + + if (tmp) + { + it->current = tmp; + goto deliver; + } + + runner = _fetch_parent_widget(runner); + } + while(runner && runner != it->origin); + + //Reaching this point here means that there is no widget left, as there is no more parent we can explore + it->current = NULL; + +deliver: + *data = (void*)it->current; + return !!it->current; +} + +EAPI Eina_Iterator* +efl_ui_widget_tree_iterator(Efl_Ui_Widget *obj) +{ + ELM_WIDGET_DATA_GET_OR_RETURN(obj, pd, NULL); + Widget_Iterator *it = iter_init(obj); + it->iterator.next = FUNC_ITERATOR_NEXT(_widget_next); + + return &it->iterator; +} + +static Eina_Bool +_only_widget(const void *container EINA_UNUSED, void *data, void *fdata EINA_UNUSED) +{ + return efl_isa(data, EFL_UI_WIDGET_CLASS); +} + +EAPI Eina_Iterator* +efl_ui_widget_tree_widget_iterator(Efl_Ui_Widget *obj) +{ + Eina_Iterator *tree_iterator = efl_ui_widget_tree_iterator(obj); + ELM_WIDGET_DATA_GET_OR_RETURN(obj, pd, NULL); + + return eina_iterator_filter_new(tree_iterator, _only_widget, NULL, NULL); +} + +static Eina_Bool +_parent_next(Widget_Iterator *it, void **data) +{ + if (!it->current) + { + *data = it->origin; + it->current = *data; + } + else + { + Efl_Ui_Widget *parent = efl_ui_widget_parent_get(it->current); + + *data = parent; + it->current = parent; + } + + return !!*data; +} + +EAPI Eina_Iterator* +efl_ui_widget_parent_iterator(Efl_Ui_Widget *obj) +{ + ELM_WIDGET_DATA_GET_OR_RETURN(obj, pd, NULL); + Widget_Iterator *it = iter_init(obj); + it->iterator.next = FUNC_ITERATOR_NEXT(_parent_next); + return &it->iterator; +} diff --git a/src/lib/elementary/efl_ui_widget_common.h b/src/lib/elementary/efl_ui_widget_common.h new file mode 100644 index 0000000000..0e538cbed5 --- /dev/null +++ b/src/lib/elementary/efl_ui_widget_common.h @@ -0,0 +1,35 @@ +#ifndef EFL_UI_WIDGET_COMMON_H +#define EFL_UI_WIDGET_COMMON_H + +/** + * @brief Get an iterator over all subelements located at obj. + * + * This iterator contains also the canvas objects which are part of the widgets, + * be aware that the construction of this tree is internal and might change heavily + * inbetween versions. + * + * @param obj The widget which is the root of the subtree. + * + * @return A iterator that contains subelement widgets and canvas objects of the root widget. Every contained object is a Efl.Gfx.Entity. + */ +EAPI Eina_Iterator* efl_ui_widget_tree_iterator(Efl_Ui_Widget *obj); + +/** + * @brief Get an iterator over all subelements located at obj. + * + * @param obj The widget which is the root of the subtree. + * + * @return A iterator that contains subelement widgets of the root widget. Every contained object is a Efl.Ui.Widget. + */ +EAPI Eina_Iterator* efl_ui_widget_tree_widget_iterator(Efl_Ui_Widget *obj); + +/** + * @brief Get an iterator that contains all parents of the passed object. + * + * @param obj The object to fetch the parents from. + * + * @return A iterator that contains all parents of the object. Every contained object is a Efl.Ui.Widget. + */ +EAPI Eina_Iterator* efl_ui_widget_parent_iterator(Efl_Ui_Widget *obj); + +#endif diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build index 9c7cc1ed90..38ee28fe9a 100644 --- a/src/lib/elementary/meson.build +++ b/src/lib/elementary/meson.build @@ -837,6 +837,7 @@ elementary_src = [ 'elm_view_form.c', 'elm_web2.c', 'efl_ui_widget.c', + 'efl_ui_widget_common.c', 'efl_ui_win.c', 'efl_ui_win_inlined.c', 'efl_ui_win_socket.c', diff --git a/src/tests/elementary/efl_ui_suite.c b/src/tests/elementary/efl_ui_suite.c index 16f7ad3fe4..a0aa9022f8 100644 --- a/src/tests/elementary/efl_ui_suite.c +++ b/src/tests/elementary/efl_ui_suite.c @@ -19,6 +19,7 @@ static const Efl_Test_Case etc[] = { { "efl_ui_image_zoomable", efl_ui_test_image_zoomable}, { "efl_ui_layout", efl_ui_test_layout}, { "Efl_Ui_Model", efl_ui_model }, + { "efl_ui_widget", efl_ui_test_widget }, { NULL, NULL } }; diff --git a/src/tests/elementary/efl_ui_suite.h b/src/tests/elementary/efl_ui_suite.h index c337323823..e0c9089c22 100644 --- a/src/tests/elementary/efl_ui_suite.h +++ b/src/tests/elementary/efl_ui_suite.h @@ -31,6 +31,7 @@ void efl_ui_test_focus(TCase *tc); void efl_ui_test_focus_sub(TCase *tc); void efl_ui_model(TCase *tc); +void efl_ui_test_widget(TCase *tc); void loop_timer_interval_set(Eo *obj, double in); diff --git a/src/tests/elementary/efl_ui_test_widget.c b/src/tests/elementary/efl_ui_test_widget.c new file mode 100644 index 0000000000..89a429b406 --- /dev/null +++ b/src/tests/elementary/efl_ui_test_widget.c @@ -0,0 +1,182 @@ +//#define EFL_NOLEGACY_API_SUPPORT +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif +#include +#include "elm_widget.h" +#include +#include "efl_ui_suite.h" + +typedef struct { + Efl_Ui_Widget *btn1, *btn2; + Efl_Ui_Widget *box; + Efl_Ui_Widget *win; + Efl_Ui_Widget *ic; +} State; + +static void +_small_ui(State *s) +{ + s->win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(), + efl_ui_win_type_set(efl_added, EFL_UI_WIN_BASIC), + efl_text_set(efl_added, "Hello World")); + + s->ic = efl_add(EFL_UI_IMAGE_CLASS, s->win, + efl_ui_win_icon_object_set(s->win, efl_added)); + + s->box = efl_add(EFL_UI_BOX_CLASS, s->win, + efl_content_set(s->win, efl_added)); + + s->btn1 = efl_add(EFL_UI_BUTTON_CLASS, s->box, + efl_text_set(efl_added, "Quit1"), + efl_pack(s->box, efl_added)); + + s->btn2 = efl_add(EFL_UI_BUTTON_CLASS, s->box, + efl_text_set(efl_added, "Quit"), + efl_pack(s->box, efl_added)); + +} + +EFL_START_TEST(efl_ui_test_widget_parent_iterator) +{ + Eina_Iterator *it; + Efl_Ui_Widget *o; + Eina_Array *a; + State s; + + a = eina_array_new(10); + _small_ui(&s); + eina_array_push(a, s.win); + eina_array_push(a, s.box); + eina_array_push(a, s.btn1); + + it = efl_ui_widget_parent_iterator(s.btn1); + EINA_ITERATOR_FOREACH(it, o) + { + ck_assert_ptr_eq(eina_array_pop(a), o); + } + eina_iterator_free(it); + ck_assert_int_eq(eina_array_count(a), 0); +} +EFL_END_TEST + +EFL_START_TEST(efl_ui_test_widget_widget_iterator) +{ + Eina_Iterator *it; + Efl_Ui_Widget *o; + Eina_Array *a; + State s; + + a = eina_array_new(10); + _small_ui(&s); + eina_array_push(a, s.btn2); + eina_array_push(a, s.btn1); + eina_array_push(a, s.box); + eina_array_push(a, s.ic); //Hack arround the icon of the window + eina_array_push(a, s.win); + + it = efl_ui_widget_tree_widget_iterator(s.win); + EINA_ITERATOR_FOREACH(it, o) + { + Eo *c = eina_array_pop(a); + ck_assert_ptr_eq(c, o); + } + eina_iterator_free(it); + ck_assert_int_eq(eina_array_count(a), 0); +} +EFL_END_TEST + +static Evas_Object* +resize_object(Efl_Canvas_Object *o) +{ + Efl_Ui_Widget_Data *pd = efl_data_scope_safe_get(o, EFL_UI_WIDGET_CLASS); + + return eina_list_data_get(pd->subobjs); +} + +EFL_START_TEST(efl_ui_test_widget_widget_sub_iterator) +{ + Eina_Iterator *it; + Efl_Ui_Widget *o; + Eina_Array *a; + State s; + + a = eina_array_new(10); + _small_ui(&s); + eina_array_push(a, s.btn2); + eina_array_push(a, s.btn1); + eina_array_push(a, s.box); + + it = efl_ui_widget_tree_widget_iterator(s.box); + EINA_ITERATOR_FOREACH(it, o) + { + ck_assert_ptr_eq(eina_array_pop(a), o); + } + eina_iterator_free(it); + ck_assert_int_eq(eina_array_count(a), 0); +} +EFL_END_TEST + +EFL_START_TEST(efl_ui_test_widget_iterator) +{ + Eina_Iterator *it; + Efl_Ui_Widget *o; + Eina_Array *a; + State s; + + a = eina_array_new(10); + _small_ui(&s); + eina_array_push(a, resize_object(s.btn2)); + eina_array_push(a, s.btn2); + eina_array_push(a, resize_object(s.btn1)); + eina_array_push(a, s.btn1); + eina_array_push(a, resize_object(s.box)); + eina_array_push(a, s.box); + eina_array_push(a, resize_object(s.ic)); + eina_array_push(a, s.ic); + eina_array_push(a, s.win); + + it = efl_ui_widget_tree_iterator(s.win); + EINA_ITERATOR_FOREACH(it, o) + { + ck_assert_ptr_eq(eina_array_pop(a), o); + } + eina_iterator_free(it); + ck_assert_int_eq(eina_array_count(a), 0); +} +EFL_END_TEST + +EFL_START_TEST(efl_ui_test_widget_sub_iterator) +{ + Eina_Iterator *it; + Efl_Ui_Widget *o; + Eina_Array *a; + State s; + + a = eina_array_new(10); + _small_ui(&s); + eina_array_push(a, resize_object(s.btn2)); + eina_array_push(a, s.btn2); + eina_array_push(a, resize_object(s.btn1)); + eina_array_push(a, s.btn1); + eina_array_push(a, resize_object(s.box)); + eina_array_push(a, s.box); + + it = efl_ui_widget_tree_iterator(s.box); + EINA_ITERATOR_FOREACH(it, o) + { + ck_assert_ptr_eq(eina_array_pop(a), o); + } + eina_iterator_free(it); + ck_assert_int_eq(eina_array_count(a), 0); +} +EFL_END_TEST + +void efl_ui_test_widget(TCase *tc) +{ + tcase_add_test(tc, efl_ui_test_widget_parent_iterator); + tcase_add_test(tc, efl_ui_test_widget_widget_iterator); + tcase_add_test(tc, efl_ui_test_widget_widget_sub_iterator); + tcase_add_test(tc, efl_ui_test_widget_iterator); + tcase_add_test(tc, efl_ui_test_widget_sub_iterator); +} diff --git a/src/tests/elementary/meson.build b/src/tests/elementary/meson.build index f9e6a0eec2..77649a1d26 100644 --- a/src/tests/elementary/meson.build +++ b/src/tests/elementary/meson.build @@ -130,6 +130,7 @@ efl_ui_suite_src = [ 'efl_ui_test_layout.c', 'efl_ui_suite.h', 'efl_ui_model.c', + 'efl_ui_test_widget.c', ] efl_ui_suite = executable('efl_ui_suite',