forked from enlightenment/efl
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:
parent
160ece70e7
commit
32751ac33b
|
@ -52,3 +52,5 @@ tags
|
|||
*.gmo
|
||||
/config.cache-env
|
||||
/compile
|
||||
build.conf
|
||||
.gitignore
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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),
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -6,4 +6,5 @@ prefs \
|
|||
test_entry \
|
||||
test_map \
|
||||
access_output \
|
||||
datetime_input_ctxpopup
|
||||
datetime_input_ctxpopup \
|
||||
gengrid_focus_hook
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue