adding focus direction for gengrid

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
This commit is contained in:
m.zielonka 2014-03-24 16:38:27 +09:00 committed by Carsten Haitzler (Rasterman)
parent 160ece70e7
commit 32751ac33b
13 changed files with 811 additions and 6 deletions

View File

@ -52,3 +52,5 @@ tags
*.gmo
/config.cache-env
/compile
build.conf
.gitignore

View 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;

View 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;

View 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;

View 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

View 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);

View 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)
{
@ -1273,6 +1338,56 @@ test_gengrid4(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_
evas_object_show(win);
}
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)
{

View 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),

View 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);

View File

@ -15,6 +15,15 @@
* 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

View File

@ -6,4 +6,5 @@ prefs \
test_entry \
test_map \
access_output \
datetime_input_ctxpopup
datetime_input_ctxpopup \
gengrid_focus_hook

View File

@ -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

View File

@ -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
}