efl_ui_widget: introduce a new API
authorMarcel Hollerbach <mail@marcel-hollerbach.de>
Wed, 27 Feb 2019 18:29:08 +0000 (13:29 -0500)
committerWonki Kim <wonki_.kim@samsung.com>
Fri, 8 Mar 2019 11:49:36 +0000 (20:49 +0900)
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
src/lib/elementary/Efl_Ui.h
src/lib/elementary/efl_ui_widget_common.c [new file with mode: 0644]
src/lib/elementary/efl_ui_widget_common.h [new file with mode: 0644]
src/lib/elementary/meson.build
src/tests/elementary/efl_ui_suite.c
src/tests/elementary/efl_ui_suite.h
src/tests/elementary/efl_ui_test_widget.c [new file with mode: 0644]
src/tests/elementary/meson.build

index fb4ff48..0386d3d 100644 (file)
@@ -361,6 +361,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 \
@@ -841,6 +842,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 \
@@ -1692,6 +1694,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
 
index 76cd115..c1d022e 100644 (file)
@@ -172,6 +172,8 @@ EAPI void efl_ui_focus_relation_free(Efl_Ui_Focus_Relations *rel);
 
 # include <efl_ui_theme.eo.h>
 # include <efl_config_global.eo.h>
+# include <efl_ui_widget.eo.h>
+# include <efl_ui_widget_common.h>
 # include <efl_ui_widget_part.eo.h>
 # include <efl_ui_layout_base.eo.h>
 # include <efl_ui_layout.eo.h>
diff --git a/src/lib/elementary/efl_ui_widget_common.c b/src/lib/elementary/efl_ui_widget_common.c
new file mode 100644 (file)
index 0000000..0208488
--- /dev/null
@@ -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 <Elementary.h>
+
+#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 (file)
index 0000000..0e538cb
--- /dev/null
@@ -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
index b363f24..58c8dc6 100644 (file)
@@ -842,6 +842,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',
index 16f7ad3..a0aa902 100644 (file)
@@ -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 }
 };
 
index c337323..e0c9089 100644 (file)
@@ -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 (file)
index 0000000..89a429b
--- /dev/null
@@ -0,0 +1,182 @@
+//#define EFL_NOLEGACY_API_SUPPORT
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+#include <Elementary.h>
+#include "elm_widget.h"
+#include <Efl_Ui.h>
+#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);
+}
index f9e6a0e..77649a1 100644 (file)
@@ -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',