adding focus direction for gengrid
authorm.zielonka <m.zielonka@samsung.com>
Mon, 24 Mar 2014 07:38:27 +0000 (16:38 +0900)
committerCarsten Haitzler (Rasterman) <raster@rasterman.com>
Mon, 24 Mar 2014 07:38:27 +0000 (16:38 +0900)
Summary:
It is implementation of focus_direction method for gengrid widget.

We need it in our project. We must service focus by
directions and we want to use gengrid widget. Additionally we must
support changing focus by keyboard (TV remote).
Unfortunately gengrid hasn't default implementation to present its
sub-objects as candidates for focus direction.

Our solution can search for focusable sub-objects (from realized items)
and change then focused and last selected variables in gengrid. If this
candidate wins then object will receive focus.

Reviewers: cedric, raster

CC: raster, seoz
Differential Revision: https://phab.enlightenment.org/D449

13 files changed:
.gitignore
config/default/base.src
config/mobile/base.src
config/standard/base.src
configure.ac
src/bin/test.c
src/bin/test_gengrid.c
src/lib/elm_gengrid.c
src/lib/elm_gengrid_common.h
src/lib/elm_widget_gengrid.h
src/modules/Makefile.am
src/modules/gengrid_focus_hook/Makefile.am [new file with mode: 0644]
src/modules/gengrid_focus_hook/gengrid_focus_hook.c [new file with mode: 0644]

index 84e5f7e..0944c10 100644 (file)
@@ -52,3 +52,5 @@ tags
 *.gmo
 /config.cache-env
 /compile
+build.conf
+.gitignore
index c786049..2917363 100644 (file)
@@ -39,7 +39,7 @@ group "Elm_Config" struct {
   value "finger_size" int: 40;
   value "fps" double: 60.0;
   value "theme" string: "default";
-  value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api";
+  value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api:gengrid_focus_hook>gengrid_focus/api";
   value "tooltip_delay" double: 1.0;
   value "cursor_engine_only" uchar: 1;
   value "focus_highlight_enable" uchar: 0;
index 60a1521..de9111f 100644 (file)
@@ -39,7 +39,7 @@ group "Elm_Config" struct {
   value "finger_size" int: 40;
   value "fps" double: 60.0;
   value "theme" string: "default";
-  value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api";
+  value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api:gengrid_focus_hook>gengrid_focus/api";
   value "tooltip_delay" double: 1.0;
   value "cursor_engine_only" uchar: 1;
   value "focus_highlight_enable" uchar: 0;
index 838d958..88bd1b4 100644 (file)
@@ -39,7 +39,7 @@ group "Elm_Config" struct {
   value "finger_size" int: 10;
   value "fps" double: 60.0;
   value "theme" string: "default";
-  value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api";
+  value "modules" string: "prefs>prefs_iface:access_output>access/api:datetime_input_ctxpopup>datetime/api:gengrid_focus_hook>gengrid_focus/api";
   value "tooltip_delay" double: 1.0;
   value "cursor_engine_only" uchar: 1;
   value "focus_highlight_enable" uchar: 0;
index fcae239..c3149c1 100644 (file)
@@ -726,6 +726,7 @@ src/modules/Makefile
 src/modules/prefs/Makefile
 src/modules/access_output/Makefile
 src/modules/datetime_input_ctxpopup/Makefile
+src/modules/gengrid_focus_hook/Makefile
 src/modules/test_entry/Makefile
 src/modules/test_map/Makefile
 src/edje_externals/Makefile
index 1dd840c..7e6c437 100644 (file)
@@ -130,6 +130,7 @@ void test_gengrid(void *data, Evas_Object *obj, void *event_info);
 void test_gengrid2(void *data, Evas_Object *obj, void *event_info);
 void test_gengrid3(void *data, Evas_Object *obj, void *event_info);
 void test_gengrid_item_styles(void *data, Evas_Object *obj, void *event_info);
+void test_gengrid_focus_direction(void *data, Evas_Object *obj, void *event_info);
 void test_gengrid4(void *data, Evas_Object *obj, void *event_info);
 void test_gengrid_speed(void *data, Evas_Object *obj, void *event_info);
 void test_gengrid_focus(void *data, Evas_Object *obj, void *event_info);
@@ -665,6 +666,7 @@ add_tests:
    ADD_TEST(NULL, "Lists - Gengrid", "GenGrid Item Styles", test_gengrid_item_styles);
    ADD_TEST(NULL, "Lists - Gengrid", "Gengrid Update Speed", test_gengrid_speed);
    ADD_TEST(NULL, "Lists - Gengrid", "GenGrid Focus", test_gengrid_focus);
+   ADD_TEST(NULL, "Lists - Gengrid", "GenGrid Focus Direction", test_gengrid_focus_direction);
 
    //------------------------------//
    ADD_TEST(NULL, "General", "Scaling", test_scaling);
index b153767..8150ef4 100644 (file)
@@ -252,6 +252,23 @@ grid_content_get(void *data, Evas_Object *obj, const char *part)
    return NULL;
 }
 
+Evas_Object *
+grid_content_buttons_get(void *data, Evas_Object *obj, const char *part)
+{
+   const Item_Data *id = data;
+   if (!strcmp(part, "elm.swallow.icon"))
+     {
+        Evas_Object *bt = elm_button_add(obj);
+        Evas_Object *ic = elm_icon_add(obj);
+        elm_image_file_set(ic, id->path, NULL);
+        elm_image_aspect_fixed_set(ic, EINA_FALSE);
+        elm_object_part_content_set(bt, "icon", ic);
+        evas_object_show(bt);
+        return bt;
+     }
+   return NULL;
+}
+
 Eina_Bool
 grid_state_get(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, const char *part EINA_UNUSED)
 {
@@ -348,6 +365,54 @@ create_gengrid(Evas_Object *obj, int items)
    return grid;
 }
 
+static Evas_Object *
+create_gengrid_buttons(Evas_Object *obj, int items)
+{
+   Evas_Object *grid = NULL;
+   static Item_Data id[144];
+   int i, n;
+   char buf[PATH_MAX];
+
+   grid = elm_gengrid_add(obj);
+   elm_gengrid_item_size_set(grid,
+                             elm_config_scale_get() * 200,
+                             elm_config_scale_get() * 150);
+   elm_gengrid_reorder_mode_set(grid, EINA_TRUE);
+   evas_object_smart_callback_add(grid, "selected", grid_selected, NULL);
+   evas_object_smart_callback_add(grid, "clicked,double", grid_double_clicked, NULL);
+   evas_object_smart_callback_add(grid, "longpressed", grid_longpress, NULL);
+   evas_object_smart_callback_add(grid, "moved", grid_moved, NULL);
+   evas_object_smart_callback_add(grid, "drag,start,up", grid_drag_up, NULL);
+   evas_object_smart_callback_add(grid, "drag,start,right", grid_drag_right, NULL);
+   evas_object_smart_callback_add(grid, "drag,start,down", grid_drag_down, NULL);
+   evas_object_smart_callback_add(grid, "drag,start,left", grid_drag_left, NULL);
+   evas_object_smart_callback_add(grid, "drag,stop", grid_drag_stop, NULL);
+   evas_object_size_hint_weight_set(grid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+   evas_object_size_hint_align_set(grid, EVAS_HINT_FILL, EVAS_HINT_FILL);
+
+   gic = elm_gengrid_item_class_new();
+   gic->item_style = "default";
+   gic->func.text_get = grid_text_get;
+   gic->func.content_get = grid_content_buttons_get;
+   gic->func.state_get = grid_state_get;
+   gic->func.del = NULL;
+
+   n = 0;
+   for (i = 0; i < items; i++)
+     {
+        snprintf(buf, sizeof(buf), "%s/images/%s", elm_app_data_dir_get(), img[n]);
+        n = (n + 1) % 9;
+        id[i].mode = i;
+        id[i].path = eina_stringshare_add(buf);
+        id[i].item = elm_gengrid_item_append(grid, gic, &(id[i]), grid_sel, NULL);
+        if (!(i % 5))
+          elm_gengrid_item_selected_set(id[i].item, EINA_TRUE);
+     }
+   elm_gengrid_item_class_free(gic);
+
+   return grid;
+}
+
 static void
 restore_bt_clicked(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
 {
@@ -1274,6 +1339,56 @@ test_gengrid4(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_
 }
 
 void
+test_gengrid_focus_direction(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Evas_Object *win, *bt, *bxx, *bx;
+
+
+   win = elm_win_util_standard_add("gengrid", "GenGrid");
+   elm_win_autodel_set(win, EINA_TRUE);
+   evas_object_resize(win, 600, 600);
+   evas_object_show(win);
+
+   elm_win_focus_highlight_enabled_set(win, EINA_TRUE);
+   elm_win_focus_highlight_animate_set(win, EINA_TRUE);
+
+   bxx = elm_box_add(win);
+   evas_object_size_hint_weight_set(bxx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+   elm_win_resize_object_add(win, bxx);
+   evas_object_show(bxx);
+
+   bt = elm_button_add(win);
+   elm_object_text_set(bt, "Next API function");
+   elm_box_pack_end(bxx, bt);
+   evas_object_show(bt);
+
+   /* Create GenGrid */
+   Evas_Object *grid = create_gengrid_buttons(win, (12 * 12));
+   elm_box_pack_end(bxx, grid);
+   evas_object_show(grid);
+
+   elm_gengrid_focus_direction_allow_set(grid, EINA_TRUE);
+//   elm_object_focus_allow_set(grid, EINA_FALSE);
+
+   bx = elm_box_add(win);
+   elm_box_horizontal_set(bx, EINA_TRUE);
+   elm_box_pack_end(bxx, bx);
+   evas_object_show(bx);
+
+   bt = elm_button_add(win);
+   elm_object_text_set(bt, "Bring in");
+   evas_object_smart_callback_add(bt, "clicked", _btn_bring_in_clicked_cb, grid);
+   elm_box_pack_end(bx, bt);
+   evas_object_show(bt);
+
+   bt = elm_button_add(win);
+   elm_object_text_set(bt, "Show");
+   evas_object_smart_callback_add(bt, "clicked", _btn_show_clicked_cb, grid);
+   elm_box_pack_end(bx, bt);
+   evas_object_show(bt);
+}
+
+void
 test_gengrid_speed(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
 {
    Evas_Object *win, *fr, *bx;
index a362aa9..3359aba 100644 (file)
@@ -87,6 +87,27 @@ static const Evas_Smart_Cb_Description _smart_callbacks[] = {
 };
 #undef ELM_PRIV_GENGRID_SIGNALS
 
+static Gengrid_Focus_Direction_Mod_Api *gengrid_focus_direction_mod = NULL;
+
+static Gengrid_Focus_Direction_Mod_Api *
+_gengrid_focus_direction_mod_init(void)
+{
+   Elm_Module *mod = NULL;
+
+   if (!(mod = _elm_module_find_as("gengrid_focus/api"))) return NULL;
+
+   mod->api = malloc(sizeof(Gengrid_Focus_Direction_Mod_Api));
+   if (!mod->api) return NULL;
+
+   ((Gengrid_Focus_Direction_Mod_Api *)(mod->api))->hook_ptr =
+     _elm_module_symbol_get(mod, "gen_focus_direction");
+
+   if (!((Gengrid_Focus_Direction_Mod_Api *)(mod->api))->hook_ptr)
+     return NULL;
+
+   return mod->api;
+}
+
 static void
 _item_show_region(void *data)
 {
@@ -550,6 +571,34 @@ _elm_gengrid_item_unrealize(Elm_Gen_Item *it,
    evas_event_thaw_eval(evas_object_evas_get(WIDGET(it)));
 }
 
+
+static void
+_elm_gengrid_item_focused_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Elm_Gen_Item *it = data;
+   Elm_Gengrid_Smart_Data *sd = GG_IT(it)->wsd;
+   if (sd->focus_direction && gengrid_focus_direction_mod)
+     {
+        Evas_Coord x = 0;
+        Evas_Coord y = 0;
+        Evas_Coord v_w = 0;
+        Evas_Coord v_h = 0;
+        Evas_Coord step_x = 0;
+        Evas_Coord step_y = 0;
+        Evas_Coord page_x = 0;
+        Evas_Coord page_y = 0;
+
+        eo_do(sd->obj,
+            elm_interface_scrollable_content_pos_get(&x, &y),
+            elm_interface_scrollable_step_size_get(&step_x, &step_y),
+            elm_interface_scrollable_page_size_get(&page_x, &page_y),
+            elm_interface_scrollable_content_viewport_size_get(&v_w, &v_h));
+
+        elm_gengrid_item_selected_set((Elm_Object_Item *)it, EINA_TRUE);
+        elm_gengrid_item_show((Elm_Object_Item *)it, ELM_GENGRID_ITEM_SCROLLTO_IN);
+     }
+}
+
 static void
 _item_mouse_up_cb(void *data,
                   Evas *evas EINA_UNUSED,
@@ -816,6 +865,7 @@ _item_realize(Elm_Gen_Item *it)
                   edje_object_part_swallow(VIEW(it), key, ic);
                   evas_object_show(ic);
                   elm_widget_sub_object_add(WIDGET(it), ic);
+                  evas_object_smart_callback_add(ic, "focused", _elm_gengrid_item_focused_cb, it);
                }
           }
      }
@@ -1914,6 +1964,8 @@ _elm_gengrid_smart_event(Eo *obj, void *_pd, va_list *list)
    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
    if (!sd->items) return;
 
+   if (sd->focus_direction && gengrid_focus_direction_mod) return;
+
    eo_do(obj,
          elm_interface_scrollable_content_pos_get(&x, &y),
          elm_interface_scrollable_step_size_get(&step_x, &step_y),
@@ -2173,7 +2225,13 @@ _elm_gengrid_smart_on_focus(Eo *obj, void *_pd EINA_UNUSED, va_list *list)
    Elm_Object_Item *it = NULL;
    Eina_Bool is_sel = EINA_FALSE;
 
+   if (sd->focus_direction && gengrid_focus_direction_mod)
+     {
+        if (ret) *ret = EINA_TRUE;
+        return;
+     }
    eo_do_super(obj, MY_CLASS, elm_obj_widget_on_focus(&int_ret));
+   
    if (!int_ret) return;
 
    if (elm_widget_focus_get(obj) && (sd->selected) &&
@@ -2228,10 +2286,28 @@ _elm_gengrid_smart_focus_next_manager_is(Eo *obj EINA_UNUSED, void *_pd EINA_UNU
 }
 
 static void
-_elm_gengrid_smart_focus_direction_manager_is(Eo *obj EINA_UNUSED, void *_pd EINA_UNUSED, va_list *list)
+_elm_gengrid_smart_focus_direction_manager_is(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
 {
    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
+   Elm_Gengrid_Smart_Data *sd = _pd;
    *ret = EINA_FALSE;
+   if (sd->focus_direction && gengrid_focus_direction_mod)
+     *ret = EINA_TRUE;
+}
+
+static void
+_elm_gengrid_smart_focus_direction(Eo *obj, void *_pd, va_list *list)
+{
+   Elm_Gengrid_Smart_Data *sd = _pd;
+   if (sd->focus_direction && gengrid_focus_direction_mod)
+     {
+        gengrid_focus_direction_mod->hook_ptr(obj, _pd, list);
+     }
+   else
+     {
+        Eina_Bool *ret = va_arg(*list, Eina_Bool *);
+        *ret = EINA_FALSE;
+     }
 }
 
 static void
@@ -2737,6 +2813,7 @@ _elm_gengrid_smart_add(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
                              elm_widget_style_get(obj)))
      CRI("Failed to set layout!");
 
+   if (!gengrid_focus_direction_mod) gengrid_focus_direction_mod = _gengrid_focus_direction_mod_init();
    eo_do(obj, elm_interface_scrollable_objects_set(wd->resize_obj, priv->hit_rect));
 
    priv->old_h_bounce = bounce;
@@ -2744,6 +2821,7 @@ _elm_gengrid_smart_add(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
 
    eo_do(obj, elm_interface_scrollable_bounce_allow_set(bounce, bounce));
 
+
    eo_do(obj,
          elm_interface_scrollable_animate_start_cb_set
          (_scroll_animate_start_cb),
@@ -2760,6 +2838,7 @@ _elm_gengrid_smart_add(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
    priv->align_x = 0.5;
    priv->align_y = 0.5;
    priv->highlight = EINA_TRUE;
+   priv->focus_direction = EINA_FALSE;
 
    priv->pan_obj = eo_add(MY_PAN_CLASS, evas_object_evas_get(obj));
    pan_data = eo_data_scope_get(priv->pan_obj, MY_PAN_CLASS);
@@ -3247,6 +3326,15 @@ elm_gengrid_horizontal_set(Evas_Object *obj,
    eo_do(obj, elm_obj_gengrid_horizontal_set(horizontal));
 }
 
+EAPI void
+elm_gengrid_focus_direction_allow_set(Evas_Object *obj,
+                                      Eina_Bool flag)
+{
+  ELM_GENGRID_CHECK(obj);
+  ELM_GENGRID_DATA_GET(obj, sd);
+  sd->focus_direction = flag; 
+}
+
 static void
 _horizontal_set(Eo *obj, void *_pd, va_list *list)
 {
@@ -4136,7 +4224,6 @@ elm_gengrid_item_show(Elm_Object_Item *item,
 
    ELM_GENGRID_ITEM_CHECK_OR_RETURN(it);
    sd = GG_IT(it)->wsd;
-
    if ((it->generation < sd->generation)) return;
 
    sd->show_region = EINA_TRUE;
@@ -4484,6 +4571,7 @@ _class_constructor(Eo_Class *klass)
         EO_OP_FUNC(ELM_OBJ_WIDGET_ID(ELM_OBJ_WIDGET_SUB_ID_FOCUS_DIRECTION_MANAGER_IS), _elm_gengrid_smart_focus_direction_manager_is),
         EO_OP_FUNC(ELM_OBJ_WIDGET_ID(ELM_OBJ_WIDGET_SUB_ID_FOCUS_HIGHLIGHT_GEOMETRY_GET), _elm_gengrid_focus_highlight_geometry_get),
         EO_OP_FUNC(ELM_OBJ_WIDGET_ID(ELM_OBJ_WIDGET_SUB_ID_FOCUSED_ITEM_GET), _elm_gengrid_focused_item_get),
+        EO_OP_FUNC(ELM_OBJ_WIDGET_ID(ELM_OBJ_WIDGET_SUB_ID_FOCUS_DIRECTION), _elm_gengrid_smart_focus_direction),
 
         EO_OP_FUNC(ELM_OBJ_LAYOUT_ID(ELM_OBJ_LAYOUT_SUB_ID_SIZING_EVAL), _elm_gengrid_smart_sizing_eval),
 
index be77038..eedc076 100644 (file)
@@ -565,3 +565,13 @@ EAPI void                          elm_gengrid_item_select_mode_set(Elm_Object_I
  */
 EAPI Elm_Object_Select_Mode        elm_gengrid_item_select_mode_get(const Elm_Object_Item *it);
 
+
+/**
+ * Set if gengrid should manage focus direction.
+ *
+ * @param obj The gengrid object
+ * @param flag The state which should be set.
+ *
+ * @ingroup Gengrid
+ */
+EAPI void elm_gengrid_focus_direction_allow_set(Evas_Object *obj, Eina_Bool flag);
index f551f84..9be4ab7 100644 (file)
  * other widgets which are a gengrid with some more logic on top.
  */
 
+
+typedef struct _Gengrid_Focus_Direction_Mod_Api Gengrid_Focus_Direction_Mod_Api;
+
+struct _Gengrid_Focus_Direction_Mod_Api
+{
+  eo_op_func_type hook_ptr;
+};
+
+
 /**
  * Base widget smart data extended with gengrid instance data.
  */
@@ -110,6 +119,8 @@ struct _Elm_Gengrid_Smart_Data
    Eina_Bool                             show_region : 1;
    Eina_Bool                             bring_in : 1;
    Eina_Bool                             mouse_down : 1; /**< a flag that mouse is down on the list at the moment. this flag is set to true on mouse and reset to false on mouse up */
+   /* a flag for focus direction. by default it is false */
+   Eina_Bool                             focus_direction : 1;
 };
 
 struct Elm_Gen_Item_Type
index 00fbcc0..7b96970 100644 (file)
@@ -6,4 +6,5 @@ prefs \
 test_entry \
 test_map \
 access_output \
-datetime_input_ctxpopup
+datetime_input_ctxpopup \
+gengrid_focus_hook
diff --git a/src/modules/gengrid_focus_hook/Makefile.am b/src/modules/gengrid_focus_hook/Makefile.am
new file mode 100644 (file)
index 0000000..54d5b48
--- /dev/null
@@ -0,0 +1,38 @@
+
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-DELM_INTERNAL_API_ARGESFSDFEFC=1 \
+-I. \
+-I$(top_builddir) \
+-I$(top_srcdir) \
+-I$(top_srcdir)/src/lib \
+-I$(top_builddir)/src/lib \
+-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
+-DPACKAGE_LIB_DIR=\"$(libdir)\" \
+@ELEMENTARY_CFLAGS@ \
+@ELEMENTARY_X_CFLAGS@ \
+@ELEMENTARY_FB_CFLAGS@ \
+@ELEMENTARY_SDL_CFLAGS@ \
+@ELEMENTARY_WIN32_CFLAGS@ \
+@ELEMENTARY_WINCE_CFLAGS@ \
+@ELEMENTARY_ELOCATION_CFLAGS@ \
+@ELEMENTARY_EWEATHER_CFLAGS@ \
+@ELEMENTARY_WEB_CFLAGS@ \
+@ELEMENTARY_EMAP_CFLAGS@ \
+@ELEMENTARY_WAYLAND_CFLAGS@ \
+@ELEMENTARY_EMAP_CFLAGS@ \
+@EVIL_CFLAGS@
+
+if ELEMENTARY_WINDOWS_BUILD
+AM_CPPFLAGS += -DELEMENTARY_BUILD
+endif
+
+pkgdir = $(libdir)/elementary/modules/gengrid_focus_hook/$(MODULE_ARCH)
+pkg_LTLIBRARIES = module.la
+
+module_la_SOURCES = gengrid_focus_hook.c
+
+module_la_LIBADD = @ELEMENTARY_LIBS@ $(top_builddir)/src/lib/libelementary.la
+module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version
+module_la_LIBTOOLFLAGS = --tag=disable-static
diff --git a/src/modules/gengrid_focus_hook/gengrid_focus_hook.c b/src/modules/gengrid_focus_hook/gengrid_focus_hook.c
new file mode 100644 (file)
index 0000000..8df8400
--- /dev/null
@@ -0,0 +1,537 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+#include <Elementary.h>
+#include <Elementary_Cursor.h>
+#include "elm_priv.h"
+#include "elm_widget_gengrid.h"
+
+#include <limits.h>
+
+#define GG_IT(_it) (_it->item)
+
+#define EINA_INLIST_REVERSE_FOREACH_INSIDE(list, it)                         \
+  for (it = NULL, it = (list ? _EINA_INLIST_CONTAINER(it, list) : NULL); it; \
+       it = (EINA_INLIST_GET(it)->prev                                       \
+                 ? _EINA_INLIST_CONTAINER(it, EINA_INLIST_GET(it)->prev)     \
+                 : NULL))
+
+#define EINA_LIST_REVERSE_FOREACH_INSIDE(list, l, data) \
+  for (l = list, data = eina_list_data_get(l); l;       \
+       l = eina_list_prev(l), data = eina_list_data_get(l))
+
+#define WEIGHT_MAX ((double)INT_MAX / (double)1000000)
+
+/**
+ * Find the first parent of object.
+ * Parent must be type of elm_gengrid.
+ *
+ * @param base
+ *
+ * @return
+ */
+static const Evas_Object *
+_find_gengrid_parent_item(const Evas_Object *base)
+{
+  const Evas_Object *res = NULL;
+  if (base)
+    {
+       const Evas_Object *parent = NULL;
+       parent = elm_widget_parent_get(base);
+
+       while (parent)
+         {
+            if (evas_object_smart_type_check(parent, "elm_gengrid"))
+              {
+                 res = parent;
+                 break;
+              }
+            parent = elm_widget_parent_get(parent);
+         }
+    }
+  return res;
+}
+
+static Eina_Bool
+_can_focus(const Evas_Object *obj)
+{
+  if (obj && elm_object_focus_allow_get(obj) &&
+      elm_widget_can_focus_get(obj))
+    return EINA_TRUE;
+  return EINA_FALSE;
+}
+
+
+static Eina_List *
+_get_contents(const Evas_Object *obj)
+{
+  Eina_List *res = NULL;
+  if (!obj)
+    return NULL;
+
+  if (evas_object_smart_type_check(obj, "elm_layout"))
+    {
+       Evas_Object *edje = elm_layout_edje_get(obj);
+       if (edje)
+         {
+            const Eina_List *l = NULL;
+            const char *key;
+            const Eina_List *contents =
+                elm_widget_stringlist_get(
+                    edje_object_data_get(edje, "contents"));
+
+            EINA_LIST_FOREACH(contents, l, key)
+              {
+                 if (key)
+                   {
+                      const Evas_Object *child = 
+                          edje_object_part_swallow_get(edje, key);
+                      if (child)
+                        {
+                           Eina_List *l1 = NULL;
+                           res = eina_list_append(res, child);
+
+                           l1 = _get_contents(child);
+                           if (l1)
+                             {
+                                res = eina_list_merge(res, l1);
+                             }
+                        }
+                   }
+              }
+         }
+    }
+  return res;
+}
+
+static Eina_List *
+_gengrid_item_contents_get(const Elm_Gen_Item *it)
+{
+  Eina_List *res = NULL;
+  Eina_List *l = NULL;
+  Evas_Object *child = NULL;
+  EINA_LIST_FOREACH(it->content_objs, l, child)
+    {
+       if (child)
+         {
+            Eina_List *l1 = NULL;
+            res = eina_list_append(res, child);
+
+            l1 = _get_contents(child);
+            if (l1)
+              {
+                 res = eina_list_merge(res, l1);
+              }
+         }
+    }
+  return res;
+}
+
+static const Evas_Object *
+_find_focusable_object(const Elm_Gen_Item *it,
+                       const Evas_Object *base,
+                       Elm_Focus_Direction dir)
+{
+  const Evas_Object *obj = NULL;
+  Eina_List *l = NULL;
+  Eina_List *contents = NULL;
+
+  if (!it) return NULL;
+
+  contents = _gengrid_item_contents_get(it);
+
+  if (contents)
+    {
+       if (base)
+         {
+            l = eina_list_data_find_list(contents, base);
+            obj = base;
+         }
+
+       if (dir == ELM_FOCUS_LEFT || dir == ELM_FOCUS_UP)
+         {
+            if (l)
+              l = eina_list_prev(l);
+            else
+              l = eina_list_last(contents);
+
+            while (l)
+              {
+                 obj = eina_list_data_get(l);
+                 if (_can_focus(obj))
+                   break;
+                 obj = NULL;
+                 l = eina_list_prev(l);
+              }
+         }
+       else if (dir == ELM_FOCUS_RIGHT || dir == ELM_FOCUS_DOWN)
+         {
+            if (l)
+              l = eina_list_next(l);
+            else
+              l = contents;
+            while (l)
+              {
+                 obj = eina_list_data_get(l);
+                 if (_can_focus(obj))
+                   break;
+                 obj = NULL;
+                 l = eina_list_next(l);
+              }
+         }
+       eina_list_free(contents);
+  }
+  return obj;
+}
+
+
+
+static Eina_Bool
+_check_item_contains(Elm_Gen_Item *it, const Evas_Object *base)
+{
+  Eina_Bool res = EINA_FALSE;
+  Eina_List *contents = NULL;
+  contents = _gengrid_item_contents_get(it);
+
+  if (contents)
+    {
+       const Eina_List *l = NULL;
+       Evas_Object *content = NULL;
+
+       // loop contents (Evas_Object) of item
+       EINA_LIST_FOREACH(contents, l, content)
+         {
+            if (content == base)
+              {
+                 res = EINA_TRUE;
+                 break;
+              }
+         }
+       eina_list_free(contents);
+    }
+  return res;
+}
+
+static Elm_Gen_Item *
+_find_item_for_base(const Evas_Object *obj,
+                    const Evas_Object *base)
+{
+  ELM_GENGRID_CHECK(obj) EINA_FALSE;
+  ELM_GENGRID_DATA_GET(obj, sd);
+  Elm_Gen_Item *res = NULL;
+  Elm_Gen_Item *it = (Elm_Gen_Item *)(sd->last_selected_item);
+
+  if (it && _check_item_contains(it, base))
+    {
+       res = it;
+    }
+  else
+    {  // try find in all
+       EINA_INLIST_FOREACH(sd->items, it)
+         {
+            if (it && _check_item_contains(it, base))
+              {
+                 res = it;
+                 break;
+              }
+         }
+    }
+  return res;
+}
+
+static Eina_Bool
+_gengrid_self_focus_item_get(
+    const Evas_Object *obj, const Evas_Object *base,
+    // list of Elm_Gen_Items
+    const Eina_List *items, void *(*list_data_get)(const Eina_List *l),
+    double degree, Evas_Object **direction, double *weight)
+{
+  Evas_Coord ox, oy;
+  Evas_Coord vw, vh;
+  const Evas_Object *res_obj = NULL;
+  Elm_Focus_Direction dir = ELM_FOCUS_UP;
+
+  unsigned int items_count = 0;
+  unsigned int columns = 0, items_visible = 0;
+  unsigned int items_row = 0, items_col = 0, rows = 0;
+  int new_position = 0;
+  int cx = 0;
+  int cy = 0;
+
+  int focused_pos = 0;
+
+  Elm_Gen_Item *it = NULL;
+  Elm_Gen_Item *it_res = NULL;
+  Eina_List *list = NULL;
+  Eina_List *l = NULL;
+
+  Elm_Gen_Item *focused_item = NULL;
+
+  (void)list_data_get;
+
+  ELM_GENGRID_CHECK(obj) EINA_FALSE;
+  ELM_GENGRID_DATA_GET(obj, sd);
+
+  if (!direction || !weight || !base || !items) return EINA_FALSE;
+
+  evas_object_geometry_get(sd->pan_obj, &ox, &oy, &vw, &vh);
+
+  focused_item = _find_item_for_base(obj, base);
+
+  if (!focused_item) return EINA_FALSE;
+
+
+  if (degree == 0)
+    dir = ELM_FOCUS_UP;
+  else if (degree == 90)
+    dir = ELM_FOCUS_RIGHT;
+  else if (degree == 180)
+    dir = ELM_FOCUS_DOWN;
+  else if (degree == 270)
+    dir = ELM_FOCUS_LEFT;
+  else
+    return EINA_FALSE;
+
+  res_obj = _find_focusable_object(focused_item, base, dir);
+  if (res_obj && res_obj != base)
+    {
+       *direction = (Evas_Object *)res_obj;
+       *weight = WEIGHT_MAX;
+       return EINA_TRUE;
+    }
+
+  focused_pos = focused_item->position - 1;
+
+  items_count =
+      sd->item_count - eina_list_count(sd->group_items) + sd->items_lost;
+  if (sd->horizontal)
+    {
+       if (sd->item_height > 0) items_visible = vh / sd->item_height;
+       if (items_visible < 1) items_visible = 1;
+
+       columns = items_count / items_visible;
+       if (items_count % items_visible) columns++;
+
+       items_row = items_visible;
+       if (items_row > sd->item_count) items_row = sd->item_count;
+
+       cx = focused_pos / items_row;
+       cy = focused_pos % items_row;
+    }
+  else
+    {
+       if (sd->item_width > 0) items_visible = vw / sd->item_width;
+       if (items_visible < 1) items_visible = 1;
+
+       rows = items_count / items_visible;
+       if (items_count % items_visible) rows++;
+
+       items_col = items_visible;
+       if (items_col > sd->item_count) items_col = sd->item_count;
+
+       cy = focused_pos / items_col;
+       cx = focused_pos % items_col;
+    }
+  
+  
+  if (dir == ELM_FOCUS_UP)
+    cy--;
+  else if (dir == ELM_FOCUS_RIGHT)
+    cx++;
+  else if (dir == ELM_FOCUS_DOWN)
+    cy++;
+  else if (dir == ELM_FOCUS_LEFT)
+    cx--;
+
+  if (cx < 0 || cy < 0) return EINA_FALSE;
+
+  if (sd->horizontal)
+    {
+       if ((cy > (items_row - 1)) || (cx > (columns - 1))) return EINA_FALSE;
+       new_position = items_row * cx + cy;
+    }
+  else
+   {
+       if ((cx > (items_col - 1)) || (cy > (rows - 1))) return EINA_FALSE;
+       new_position = cx + items_col * cy;
+  }
+
+  if (new_position > (items_count - 1)) return EINA_FALSE;
+
+  focused_pos++;
+  new_position++;
+
+  list = eina_list_data_find_list(items, focused_item);
+
+  if (!list) return EINA_FALSE;
+
+  if (new_position > focused_pos)
+    {
+       /// New position should be after focused
+       EINA_LIST_FOREACH(list, l, it)
+         {
+            if (it->position == new_position)
+              {
+                 it_res = it;
+                 break;
+              }
+         }
+    }
+  else if (new_position < focused_pos)
+    {
+       /// New position should be before focused
+       EINA_LIST_REVERSE_FOREACH_INSIDE(list, l, it)
+         {
+         if (it->position == new_position)
+           {
+              it_res = it;
+              break;
+           }
+         }
+    }
+  else
+   {
+      return EINA_FALSE;
+   }
+
+  if (it_res)
+    {
+       res_obj = _find_focusable_object(it_res, base, dir);
+       *direction = (Evas_Object *)res_obj;
+       *weight = WEIGHT_MAX;
+       return EINA_TRUE;
+    }
+  return EINA_FALSE;
+}
+
+static Eina_Bool
+_gengrid_focus_list_direction_get(
+    const Evas_Object *obj, const Evas_Object *base,
+    // list of Elm_Gen_Items
+    const Eina_List *items, void *(*list_data_get)(const Eina_List *l),
+    double degree, Evas_Object **direction, double *weight)
+{
+  const Eina_List *l = NULL;
+  Evas_Object *current_best = NULL;
+  (void)list_data_get;
+  ELM_GENGRID_CHECK(obj) EINA_FALSE;
+  if (!direction || !weight || !base || !items) return EINA_FALSE;
+
+  l = items;
+  current_best = *direction;
+
+  // loop items Elm_Gen_Item
+  for (; l; l = eina_list_next(l))
+    {
+       Eina_List *contents = NULL;
+       Elm_Gen_Item *it = list_data_get(l);
+       contents = _gengrid_item_contents_get(it);
+       if (contents)
+         {
+            const Eina_List *l2 = NULL;
+            Evas_Object *content = NULL;
+
+            // loop contents (Evas_Object) of item
+            EINA_LIST_FOREACH(contents, l2, content)
+              {
+                 // if better element than set new sd->focused and sd->selected
+                 elm_widget_focus_direction_get(content, base, degree, direction,
+                                                weight);
+              }
+            eina_list_free(contents);
+         }
+    }
+  if (current_best != *direction)
+    return EINA_TRUE;
+  else
+    return EINA_FALSE;
+}
+
+EAPI void
+gen_focus_direction(Eo *obj, void *_pd, va_list *list)
+{
+  Eina_List *items = NULL;
+  const Evas_Object *parent = NULL;
+
+  void *(*list_data_get)(const Eina_List * list);
+  Eina_List *(*list_free)(Eina_List * list);
+
+  Elm_Gengrid_Smart_Data *sd = _pd;
+
+  Evas_Object *base = va_arg(*list, Evas_Object *);
+  double degree = va_arg(*list, double);
+  Evas_Object **direction = va_arg(*list, Evas_Object **);
+  double *weight = va_arg(*list, double *);
+  Eina_Bool *ret = va_arg(*list, Eina_Bool *);
+
+  if (!sd)
+    {
+       *ret = EINA_FALSE;
+       return;
+    }
+
+
+  *ret = EINA_FALSE;
+  list_data_get = NULL;
+
+  Eina_Bool (*list_direction_get)(
+      const Evas_Object * obj, const Evas_Object * base,
+      const Eina_List * items, void * (*list_data_get)(const Eina_List * l),
+      double degree, Evas_Object * *direction, double * weight);
+
+  list_direction_get = NULL;
+  list_free = NULL;
+  parent = _find_gengrid_parent_item(base);
+
+  /// If focused is subobject of this gengrid then we selected next
+  /// in direction
+  if (obj == parent)
+    {
+       items = elm_gengrid_realized_items_get(obj);
+       list_data_get = NULL;
+       list_direction_get = _gengrid_self_focus_item_get;
+       list_free = eina_list_free;
+    }
+  
+  if (!items)
+    {
+       items = (Eina_List *)(elm_object_focus_custom_chain_get(obj));
+       list_data_get = eina_list_data_get;
+       list_direction_get = elm_widget_focus_list_direction_get;
+       list_free = NULL;
+    }
+
+  if (!items)
+    {
+       items = elm_gengrid_realized_items_get(obj);
+       list_data_get = eina_list_data_get;
+       list_direction_get = _gengrid_focus_list_direction_get;
+       list_free = eina_list_free;
+    }
+
+  if (!items)
+    {
+       *ret = EINA_FALSE;
+       return;
+    }
+
+  *ret = list_direction_get(obj, base, items, list_data_get, degree, direction,
+                            weight);
+
+  if (list_free) list_free(items);
+}
+
+// module api funcs needed
+EAPI int
+elm_modapi_init(void *m)
+{
+   (void) m;
+   return 1; // succeed always
+}
+
+EAPI int
+elm_modapi_shutdown(void *m)
+{
+   (void) m;
+   return 1; // succeed always
+}