elementary: Focus improvement for elm_genlist and elm_list

Summary:
This patch applies automatic focus feature to elm_genlist and elm_list
containers.
Currently (prior to this patch), focusable widgets inside list items of both
containers receive focus by an explicit mouse click over them, and lose focus
when focus goes to someone else in the window.
This change also adds the ability to:
- focus by default on the first-from-right focusable widget upon items selection
- lose the focus when another item is selected (focused or not)
- move focus between focusable widgets inside the same item by left and right
arrow keys accordingly (up and down keys when elm_list is in horizontal mode)

Focus is supported for horizontal and vertical lists.

Tests have been added for genlists and lists to check focus feature.
This commit is contained in:
Yossi Kantor 2013-02-20 12:00:49 +02:00 committed by Daniel Zaoui
parent 698063a988
commit 2e1eacd0ba
12 changed files with 643 additions and 5 deletions

View File

@ -78,6 +78,8 @@ void test_list4(void *data, Evas_Object *obj, void *event_info);
void test_list5(void *data, Evas_Object *obj, void *event_info);
void test_list6(void *data, Evas_Object *obj, void *event_info);
void test_list7(void *data, Evas_Object *obj, void *event_info);
void test_list8(void *data, Evas_Object *obj, void *event_info);
void test_list9(void *data, Evas_Object *obj, void *event_info);
void test_list_separator(void *data, Evas_Object *obj, void *event_info);
void test_inwin(void *data, Evas_Object *obj, void *event_info);
void test_inwin2(void *data, Evas_Object *obj, void *event_info);
@ -104,6 +106,7 @@ void test_genlist16(void *data, Evas_Object *obj, void *event_info);
void test_genlist17(void *data, Evas_Object *obj, void *event_info);
void test_genlist18(void *data, Evas_Object *obj, void *event_info);
void test_genlist19(void *data, Evas_Object *obj, void *event_info);
void test_genlist20(void *data, Evas_Object *obj, void *event_info);
void test_genlist_item_styles(void *data, Evas_Object *obj, void *event_info);
void test_gesture_layer(void *data, Evas_Object *obj, void *event_info);
void test_gesture_layer2(void *data, Evas_Object *obj, void *event_info);
@ -365,7 +368,7 @@ my_win_main(char *autorun, Eina_Bool test_win_only)
* type is a basic window (the final parameter).
* You can call elm_win_util_standard_add() instead. This is a convenient API
* for window and bg creation. You don't need to create bg object manually.
* You can also set the title of the window at the same time.
* You can also set the title of the window at the same time.
* ex) win = elm_win_util_standard_add("main", "Elementary Tests"); */
win = elm_win_add(NULL, "main", ELM_WIN_BASIC);
/* Set the title of the window - This is in the titlebar. */
@ -591,6 +594,8 @@ add_tests:
ADD_TEST(NULL, "Lists", "List 5", test_list5);
ADD_TEST(NULL, "Lists", "List 6", test_list6);
ADD_TEST(NULL, "Lists", "List 7", test_list7);
ADD_TEST(NULL, "Lists", "List Focus", test_list8);
ADD_TEST(NULL, "Lists", "List Focus Horizontal", test_list9);
ADD_TEST(NULL, "Lists", "List Separator", test_list_separator);
ADD_TEST(NULL, "Lists", "Genlist", test_genlist);
ADD_TEST(NULL, "Lists", "Genlist 2", test_genlist2);
@ -614,6 +619,7 @@ add_tests:
ADD_TEST(NULL, "Lists", "Genlist Decorate Modes", test_genlist17);
ADD_TEST(NULL, "Lists", "Genlist Tree and Decorate All Mode", test_genlist18);
ADD_TEST(NULL, "Lists", "Genlist Full Widget", test_genlist19);
ADD_TEST(NULL, "Lists", "Genlist Focus", test_genlist20);
ADD_TEST(NULL, "Lists", "Genlist Item Styles", test_genlist_item_styles);
ADD_TEST(NULL, "Lists", "GenGrid", test_gengrid);
ADD_TEST(NULL, "Lists", "GenGrid 2", test_gengrid2);

View File

@ -3230,6 +3230,152 @@ test_genlist19(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_i
evas_object_show(win);
}
static unsigned _gl20_objects = 5;
static const char *_gl20_object_names[] = {"None", "Square", "Button", "Check", "Box"};
static char *
gl20_text_get(void *data, Evas_Object *obj __UNUSED__, const char *part __UNUSED__)
{
char buf[256];
char op = (uintptr_t)data % 100;
snprintf(buf, sizeof(buf), " %s / %s ",
_gl20_object_names[op / 10],
_gl20_object_names[op % 10]);
return strdup(buf);
}
static Evas_Object *
gl20_content_get(void *data, Evas_Object *obj, const char *part)
{
Evas_Object *cnt = NULL;
char op = (uintptr_t)data % 100;
char type = (!strcmp(part,"elm.swallow.icon")) ? op / 10 : op % 10;
switch(type)
{
case 1:
cnt = elm_bg_add(obj);
evas_object_color_set(cnt, 128, 18, 128, 255);
break;
case 2:
cnt = elm_button_add(obj);
break;
case 3:
cnt = elm_check_add(obj);
break;
case 4:
cnt = elm_box_add(obj);
evas_object_size_hint_align_set(cnt, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_horizontal_set(cnt, EINA_TRUE);
elm_box_pack_end(cnt, gl20_content_get((void *)2, obj, ""));
elm_box_pack_end(cnt, gl20_content_get((void *)3, obj, ""));
elm_box_pack_end(cnt, gl20_content_get((void *)2, obj, ""));
break;
default:
break;
}
if (cnt)
{
evas_object_size_hint_weight_set(cnt, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_show(cnt);
}
return cnt;
}
static void
gl20_focus_on_selection_set(Evas_Object *gl, Evas_Object *chk, Eina_Bool focus)
{
elm_genlist_focus_on_selection_set(gl, focus);
elm_check_state_set(chk, focus);
printf("genlist_focus_on_selection = %s\n", (focus) ? "true" : "false");
}
static void
gl20_focus_check_changed(void *data, Evas_Object *obj, void *event_info __UNUSED__)
{
Eina_Bool nextstate = !elm_genlist_focus_on_selection_get(data);
gl20_focus_on_selection_set(data, obj, nextstate);
}
void
test_genlist20(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
{
Evas_Object *win, *gl, *bx, *bxx, *chk;
unsigned lhand, rhand;
elm_config_focus_highlight_enabled_set(EINA_TRUE);
win = elm_win_util_standard_add("genlist-focus", "Genlist Focus");
elm_win_autodel_set(win, EINA_TRUE);
bxx = elm_box_add(win);
elm_win_resize_object_add(win, bxx);
evas_object_size_hint_weight_set(bxx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_show(bxx);
gl = elm_genlist_add(win);
evas_object_size_hint_weight_set(gl, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(gl, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_pack_end(bxx, gl);
evas_object_show(gl);
bx = elm_box_add(win);
evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, 0);
evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_horizontal_set(bx, EINA_TRUE);
evas_object_show(bx);
chk = elm_check_add(win);
elm_object_text_set(chk, "Focus on selection");
evas_object_smart_callback_add(chk, "changed", gl20_focus_check_changed, gl);
elm_box_pack_end(bx, chk);
evas_object_show(chk);
elm_box_pack_end(bxx, bx);
gl20_focus_on_selection_set(gl, chk, EINA_TRUE);
itc1 = elm_genlist_item_class_new();
itc1->item_style = "default";
itc1->func.text_get = gl20_text_get;
itc1->func.content_get = gl20_content_get;
itc1->func.state_get = NULL;
itc1->func.del = NULL;
itc4 = elm_genlist_item_class_new();
itc4->item_style = "tree_effect";
itc4->func.text_get = gl20_text_get;
itc4->func.content_get = gl20_content_get;
itc4->func.state_get = NULL;
itc4->func.del = NULL;
for (lhand = 0; lhand < _gl20_objects; lhand++)
{
for (rhand = 0; rhand < _gl20_objects; rhand++)
{
unsigned digit1 = lhand * 10 + rhand;
elm_genlist_item_append(gl, itc1, (void*)(uintptr_t)digit1,
NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
unsigned digit2 = (_gl20_objects - lhand -1) * 10 +
(_gl20_objects - rhand -1);
elm_genlist_item_append(gl, itc1, (void*)(uintptr_t)digit2,
NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
if (rhand == (_gl20_objects - 1))
elm_genlist_item_append(gl, itc4, (void*)(uintptr_t)digit1,
NULL, ELM_GENLIST_ITEM_TREE, NULL, NULL);
}
}
elm_genlist_item_class_free(itc1);
elm_genlist_item_class_free(itc4);
evas_object_resize(win, 300, 500);
evas_object_show(win);
}
/* test genlist item styles */
const char *_genlist_styles[] = {

View File

@ -1227,6 +1227,142 @@ test_list7(void *data __UNUSED__,
evas_object_show(win);
}
static const unsigned _list_focus_objects = 5;
static const char *_list_focus_names[] = {"None", "Square", "Button", "Check", "Box"};
static const int _list_focus_combo[] = { 1, 0, 2, 33, 43, 44, 10, 30, 22, 11, 10, -1 };
static Evas_Object *
test_list8_content_get(Evas_Object *obj, unsigned type, Eina_Bool horiz)
{
Evas_Object *cnt = NULL;
switch(type)
{
case 1:
cnt = elm_bg_add(obj);
evas_object_color_set(cnt, 128, 18, 128, 255);
evas_object_size_hint_min_set(cnt, 50, 50);
break;
case 2:
cnt = elm_button_add(obj);
break;
case 3:
cnt = elm_check_add(obj);
break;
case 4:
cnt = elm_box_add(obj);
elm_box_horizontal_set(cnt, !horiz);
evas_object_size_hint_align_set(cnt, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_pack_end(cnt, test_list8_content_get(obj, 2, horiz));
elm_box_pack_end(cnt, test_list8_content_get(obj, 3, horiz));
elm_box_pack_end(cnt, test_list8_content_get(obj, 2, horiz));
break;
default:
break;
}
if (cnt)
{
evas_object_size_hint_weight_set(cnt, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_show(cnt);
}
return cnt;
}
static void
test_list8_focus_on_selection_set(Evas_Object *gl, Evas_Object *chk, Eina_Bool focus)
{
elm_list_focus_on_selection_set(gl, focus);
elm_check_state_set(chk, focus);
printf("list_focus_on_selection = %s\n", (focus) ? "true" : "false");
}
static void
test_list8_focus_check_changed(void *data, Evas_Object *obj, void *event_info __UNUSED__)
{
Eina_Bool nextstate = !elm_list_focus_on_selection_get(data);
test_list8_focus_on_selection_set(data, obj, nextstate);
}
void test_list_focus(const char *name, const char *title, Eina_Bool horiz)
{
Evas_Object *win, *li, *bx, *bxx, *chk;
unsigned lhand, rhand, idx;
char buf[256];
elm_config_focus_highlight_enabled_set(EINA_TRUE);
win = elm_win_util_standard_add(name, title);
elm_win_autodel_set(win, EINA_TRUE);
bxx = elm_box_add(win);
elm_win_resize_object_add(win, bxx);
evas_object_size_hint_weight_set(bxx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_show(bxx);
li = elm_list_add(win);
evas_object_size_hint_weight_set(li, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(li, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_list_horizontal_set(li, horiz);
elm_box_pack_end(bxx, li);
evas_object_show(li);
bx = elm_box_add(win);
evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, 0);
evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_horizontal_set(bx, EINA_TRUE);
evas_object_show(bx);
chk = elm_check_add(win);
elm_object_text_set(chk, "Focus on selection");
evas_object_smart_callback_add(chk, "changed", test_list8_focus_check_changed, li);
elm_box_pack_end(bx, chk);
evas_object_show(chk);
elm_box_pack_end(bxx, bx);
test_list8_focus_on_selection_set(li, chk, EINA_TRUE);
for (idx = 0; _list_focus_combo[idx] >= 0; idx++)
{
lhand = _list_focus_combo[idx] / 10;
rhand = _list_focus_combo[idx] % 10;
snprintf(buf, sizeof(buf), " %s / %s ",
_list_focus_names[lhand],
_list_focus_names[rhand]);
elm_list_item_append(li, buf,
test_list8_content_get(li, lhand, horiz),
test_list8_content_get(li, rhand, horiz),
NULL, NULL);
}
elm_list_go(li);
evas_object_show(li);
evas_object_resize(win, 320, 300);
evas_object_show(win);
}
void
test_list8(void *data __UNUSED__,
Evas_Object *obj __UNUSED__,
void *event_info __UNUSED__)
{
test_list_focus("list-focus", "List Focus", EINA_FALSE);
}
void
test_list9(void *data __UNUSED__,
Evas_Object *obj __UNUSED__,
void *event_info __UNUSED__)
{
test_list_focus("list-focus-horizontal", "List Focus Horizontal", EINA_TRUE);
}
void
test_list_separator(void *data __UNUSED__,
Evas_Object *obj __UNUSED__,

View File

@ -39,6 +39,7 @@ struct Elm_Gen_Item
int walking;
int generation; /**< a generation of an item. when the item is created, this value is set to the value of genlist generation. this value will be decreased when the item is going to be deleted */
const char *mouse_cursor;
Eina_List *item_focus_chain;
struct
{

View File

@ -391,6 +391,9 @@ _elm_genlist_item_unrealize(Elm_Gen_Item *it,
EINA_LIST_FREE(it->content_objs, content)
evas_object_del(content);
eina_list_free(it->item_focus_chain);
it->item_focus_chain = NULL;
it->unrealize_cb(it);
it->realized = EINA_FALSE;
@ -1687,6 +1690,18 @@ _item_realize(Elm_Gen_Item *it,
edje_object_signal_emit(VIEW(it), "elm,state,hide", "");
it->item->tree_effect_hide_me = EINA_FALSE;
}
if (it->item->type == ELM_GENLIST_ITEM_NONE)
{
Evas_Object* eobj;
Eina_List* l;
EINA_LIST_FOREACH(it->content_objs, l, eobj)
if (elm_object_focus_allow_get(eobj))
it->item_focus_chain = eina_list_append
(it->item_focus_chain, eobj);
}
evas_object_smart_callback_call(WIDGET(it), SIG_REALIZED, it);
}
@ -2357,6 +2372,50 @@ _item_single_select_down(Elm_Genlist_Smart_Data *sd)
return EINA_TRUE;
}
static void
_elm_genlist_item_focus_set(Elm_Gen_Item *it, Elm_Focus_Direction dir)
{
Evas_Object *focused_obj = NULL;
Eina_List *l;
if (!it) return;
if (!GL_IT(it)->wsd->focus_on_selection_enabled) return;
if (!it->item_focus_chain)
{
elm_object_focus_set(VIEW(it), EINA_TRUE);
return;
}
EINA_LIST_FOREACH(it->item_focus_chain, l, focused_obj)
if (elm_object_focus_get(focused_obj)) break;
if (focused_obj && (dir != ELM_FOCUS_PREVIOUS))
{
Evas_Object *nextfocus;
if (elm_widget_focus_next_get(focused_obj, dir, &nextfocus))
{
elm_object_focus_set(nextfocus, EINA_TRUE);
return;
}
}
if (!l) l = it->item_focus_chain;
if (dir == ELM_FOCUS_RIGHT)
{
l = eina_list_next(l);
if (!l) l = it->item_focus_chain;
}
else if (dir == ELM_FOCUS_LEFT)
{
l = eina_list_prev(l);
if (!l) l = eina_list_last(it->item_focus_chain);
}
elm_object_focus_set(eina_list_data_get(l), EINA_TRUE);
}
static void
_elm_genlist_smart_event(Eo *obj, void *_pd, va_list *list)
{
@ -2396,11 +2455,17 @@ _elm_genlist_smart_event(Eo *obj, void *_pd, va_list *list)
((!strcmp(ev->keyname, "KP_Left")) && (!ev->string)))
{
x -= step_x;
Elm_Gen_Item *gt = (Elm_Gen_Item*)elm_genlist_selected_item_get(obj);
_elm_genlist_item_focus_set(gt, ELM_FOCUS_LEFT);
}
else if ((!strcmp(ev->keyname, "Right")) ||
((!strcmp(ev->keyname, "KP_Right")) && (!ev->string)))
{
x += step_x;
Elm_Gen_Item *gt = (Elm_Gen_Item*)elm_genlist_selected_item_get(obj);
_elm_genlist_item_focus_set(gt, ELM_FOCUS_RIGHT);
}
else if ((!strcmp(ev->keyname, "Up")) ||
((!strcmp(ev->keyname, "KP_Up")) && (!ev->string)))
@ -3048,6 +3113,14 @@ _item_unselect(Elm_Gen_Item *it)
_item_unhighlight(it); /* unhighlight the item first */
if (!it->selected) return; /* then check whether the item is selected */
if (GL_IT(it)->wsd->focus_on_selection_enabled)
{
Evas_Object* eobj;
Eina_List* l;
EINA_LIST_FOREACH(it->item_focus_chain, l, eobj)
elm_object_focus_set(eobj, EINA_FALSE);
}
it->selected = EINA_FALSE;
sd->selected = eina_list_remove(sd->selected, it);
evas_object_smart_callback_call(WIDGET(it), SIG_UNSELECTED, it);
@ -5069,6 +5142,8 @@ _item_select(Elm_Gen_Item *it)
if (it->generation == sd->generation)
evas_object_smart_callback_call(WIDGET(it), SIG_SELECTED, it);
_elm_genlist_item_focus_set(it, ELM_FOCUS_PREVIOUS);
it->walking--;
sd->walking--;
if ((sd->clear_me) && (!sd->walking))
@ -6317,6 +6392,17 @@ elm_genlist_item_fields_update(Elm_Object_Item *item,
it->content_objs = _item_content_realize(it, VIEW(it),
&it->contents, parts);
if (it->item->type == ELM_GENLIST_ITEM_NONE)
{
Evas_Object* eobj;
Eina_List* l;
eina_list_free(it->item_focus_chain);
it->item_focus_chain = NULL;
EINA_LIST_FOREACH(it->content_objs, l, eobj)
if (elm_object_focus_allow_get(eobj))
it->item_focus_chain = eina_list_append(it->item_focus_chain, eobj);
}
if (it->flipped)
{
it->item->flip_content_objs =
@ -7341,6 +7427,39 @@ _tree_effect_enabled_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
*ret = sd->tree_effect_enabled;
}
EAPI void
elm_genlist_focus_on_selection_set(Evas_Object *obj,
Eina_Bool enabled)
{
ELM_GENLIST_CHECK(obj);
eo_do(obj, elm_obj_genlist_focus_on_selection_set(enabled));
}
static void
_focus_on_selection_set(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
{
Eina_Bool enabled = va_arg(*list, int);
Elm_Genlist_Smart_Data *sd = _pd;
sd->focus_on_selection_enabled = !!enabled;
}
EAPI Eina_Bool
elm_genlist_focus_on_selection_get(const Evas_Object *obj)
{
ELM_GENLIST_CHECK(obj) EINA_FALSE;
Eina_Bool ret = EINA_FALSE;
eo_do((Eo *) obj, elm_obj_genlist_focus_on_selection_get(&ret));
return ret;
}
static void
_focus_on_selection_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
{
Eina_Bool *ret = va_arg(*list, Eina_Bool *);
Elm_Genlist_Smart_Data *sd = _pd;
*ret = sd->focus_on_selection_enabled;
}
EAPI Elm_Object_Item *
elm_genlist_nth_item_get(const Evas_Object *obj, unsigned int nth)
{
@ -7427,6 +7546,8 @@ _class_constructor(Eo_Class *klass)
EO_OP_FUNC(ELM_OBJ_GENLIST_ID(ELM_OBJ_GENLIST_SUB_ID_HIGHLIGHT_MODE_GET), _highlight_mode_get),
EO_OP_FUNC(ELM_OBJ_GENLIST_ID(ELM_OBJ_GENLIST_SUB_ID_TREE_EFFECT_ENABLED_SET), _tree_effect_enabled_set),
EO_OP_FUNC(ELM_OBJ_GENLIST_ID(ELM_OBJ_GENLIST_SUB_ID_TREE_EFFECT_ENABLED_GET), _tree_effect_enabled_get),
EO_OP_FUNC(ELM_OBJ_GENLIST_ID(ELM_OBJ_GENLIST_SUB_ID_FOCUS_ON_SELECTION_SET), _focus_on_selection_set),
EO_OP_FUNC(ELM_OBJ_GENLIST_ID(ELM_OBJ_GENLIST_SUB_ID_FOCUS_ON_SELECTION_GET), _focus_on_selection_get),
EO_OP_FUNC_SENTINEL
};
eo_class_funcs_set(klass, func_desc);
@ -7473,6 +7594,8 @@ static const Eo_Op_Description op_desc[] = {
EO_OP_DESCRIPTION(ELM_OBJ_GENLIST_SUB_ID_HIGHLIGHT_MODE_GET, "Get whether the genlist items' should be highlighted when item selected."),
EO_OP_DESCRIPTION(ELM_OBJ_GENLIST_SUB_ID_TREE_EFFECT_ENABLED_SET, "Set Genlist tree effect."),
EO_OP_DESCRIPTION(ELM_OBJ_GENLIST_SUB_ID_TREE_EFFECT_ENABLED_GET, "Get Genlist tree effect."),
EO_OP_DESCRIPTION(ELM_OBJ_GENLIST_SUB_ID_FOCUS_ON_SELECTION_SET, "Set focus upon item's selection mode."),
EO_OP_DESCRIPTION(ELM_OBJ_GENLIST_SUB_ID_FOCUS_ON_SELECTION_GET, "Get focus upon item's selection mode."),
EO_OP_DESCRIPTION_SENTINEL
};

View File

@ -50,6 +50,8 @@ enum
ELM_OBJ_GENLIST_SUB_ID_HIGHLIGHT_MODE_GET,
ELM_OBJ_GENLIST_SUB_ID_TREE_EFFECT_ENABLED_SET,
ELM_OBJ_GENLIST_SUB_ID_TREE_EFFECT_ENABLED_GET,
ELM_OBJ_GENLIST_SUB_ID_FOCUS_ON_SELECTION_SET,
ELM_OBJ_GENLIST_SUB_ID_FOCUS_ON_SELECTION_GET,
ELM_OBJ_GENLIST_SUB_ID_LAST
};
@ -510,6 +512,29 @@ enum
*/
#define elm_obj_genlist_tree_effect_enabled_get(ret) ELM_OBJ_GENLIST_ID(ELM_OBJ_GENLIST_SUB_ID_TREE_EFFECT_ENABLED_GET), EO_TYPECHECK(Eina_Bool *, ret)
/**
* @def elm_obj_genlist_focus_on_selection_set
* @since 1.8
*
* Set focus to a first from left focusable widget upon item selection.
*
* @param[in] enabled
*
* @see elm_obj_genlist_focus_on_selection_get
*/
#define elm_obj_genlist_focus_on_selection_set(enabled) ELM_OBJ_GENLIST_ID(ELM_OBJ_GENLIST_SUB_ID_FOCUS_ON_SELECTION_SET), EO_TYPECHECK(Eina_Bool, enabled)
/**
* @def elm_obj_genlist_focus_on_selection_get
* @since 1.8
*
* Get whether the focus will be set to a widget on an item upon it's selection.
*
* @param[out] ret
*
* @see elm_obj_genlist_focus_on_selection_set
*/
#define elm_obj_genlist_focus_on_selection_get(ret) ELM_OBJ_GENLIST_ID(ELM_OBJ_GENLIST_SUB_ID_FOCUS_ON_SELECTION_GET), EO_TYPECHECK(Eina_Bool *, ret)
/**
* @}
*/

View File

@ -643,3 +643,30 @@ EAPI Eina_Bool elm_genlist_highlight_mode_get(const Evas_Object *obj);
EAPI Elm_Object_Item *
elm_genlist_nth_item_get(const Evas_Object *obj, unsigned int nth);
/**
* Set focus upon items selection mode
*
* @param obj The genlist object
* @param enabled The tree effect status
* (EINA_TRUE = enabled, EINA_FALSE = disabled)
*
* When enabled, every selection of an item inside the genlist will automatically set focus to
* its first focusable widget from the left. This is true of course if the selection was made by
* clicking an unfocusable area in an item or selecting it with a key movement. Clicking on a
* focusable widget inside an item will couse this particular item to get focus as usual.
*
* @ingroup Genlist
*/
EAPI void elm_genlist_focus_on_selection_set(Evas_Object *obj, Eina_Bool enabled);
/**
* Gets whether focus upon item's selection mode is enabled.
*
* @param obj The genlist object
* @return The tree effect status
* (EINA_TRUE = enabled, EINA_FALSE = disabled)
*
* @ingroup Genlist
*/
EAPI Eina_Bool elm_genlist_focus_on_selection_get(const Evas_Object *obj);

View File

@ -192,6 +192,67 @@ _item_single_select_down(Elm_List_Smart_Data *sd)
return EINA_TRUE;
}
static Eina_Bool
_elm_list_item_focus_set(Elm_List_Item *it, Elm_Focus_Direction dir, Eina_Bool h_mode)
{
if (!it) return EINA_FALSE;
if (!it->sd->focus_on_selection_enabled) return EINA_FALSE;
int focus_objs = 0;
Evas_Object *focus_chain[2];
Evas_Object *focused = NULL;
int idx;
if (it->icon && elm_object_focus_allow_get(it->icon))
focus_chain[focus_objs++] = it->icon;
if (it->end && elm_object_focus_allow_get(it->end))
focus_chain[focus_objs++] = it->end;
if (!focus_objs)
{
elm_object_focus_set(VIEW(it), EINA_TRUE);
return EINA_FALSE;
}
for (idx = 0; idx < focus_objs; idx++)
{
if (elm_object_focus_get(focus_chain[idx]))
{
focused = focus_chain[idx];
break;
}
}
if (!focused)
{
elm_object_focus_set(focus_chain[0], EINA_TRUE);
return EINA_FALSE;
}
if (dir != ELM_FOCUS_PREVIOUS)
{
Evas_Object *nextfocus;
if (elm_widget_focus_next_get(focused, dir, &nextfocus))
{
elm_object_focus_set(nextfocus, EINA_TRUE);
return EINA_TRUE;
}
if ((h_mode && (dir != ELM_FOCUS_UP) && (dir != ELM_FOCUS_DOWN)) ||
(!h_mode && (dir != ELM_FOCUS_LEFT) && (dir != ELM_FOCUS_RIGHT)))
return EINA_FALSE;
idx += ((dir == ELM_FOCUS_UP) || (dir == ELM_FOCUS_LEFT)) ? -1 : 1;
if (idx < 0) idx = focus_objs - 1;
if (idx >= focus_objs) idx = 0;
focused = focus_chain[idx];
}
elm_object_focus_set(focused, EINA_TRUE);
return EINA_TRUE;
}
static void
_elm_list_smart_event(Eo *obj, void *_pd, va_list *list)
{
@ -230,7 +291,10 @@ _elm_list_smart_event(Eo *obj, void *_pd, va_list *list)
if ((!strcmp(ev->keyname, "Left")) ||
((!strcmp(ev->keyname, "KP_Left")) && !ev->string))
{
if ((sd->h_mode) &&
it = (Elm_List_Item *)elm_list_selected_item_get(obj);
Eina_Bool focused = _elm_list_item_focus_set(it, ELM_FOCUS_LEFT, sd->h_mode);
if ((sd->h_mode && !focused) &&
(((evas_key_modifier_is_set(ev->modifiers, "Shift")) &&
(_item_multi_select_up(sd)))
|| (_item_single_select_up(sd))))
@ -245,7 +309,10 @@ _elm_list_smart_event(Eo *obj, void *_pd, va_list *list)
else if ((!strcmp(ev->keyname, "Right")) ||
((!strcmp(ev->keyname, "KP_Right")) && !ev->string))
{
if ((sd->h_mode) &&
it = (Elm_List_Item *)elm_list_selected_item_get(obj);
Eina_Bool focused = _elm_list_item_focus_set(it, ELM_FOCUS_RIGHT, sd->h_mode);
if ((sd->h_mode && !focused) &&
(((evas_key_modifier_is_set(ev->modifiers, "Shift")) &&
(_item_multi_select_down(sd)))
|| (_item_single_select_down(sd))))
@ -260,7 +327,10 @@ _elm_list_smart_event(Eo *obj, void *_pd, va_list *list)
else if ((!strcmp(ev->keyname, "Up")) ||
((!strcmp(ev->keyname, "KP_Up")) && !ev->string))
{
if ((!sd->h_mode) &&
it = (Elm_List_Item *)elm_list_selected_item_get(obj);
Eina_Bool focused = _elm_list_item_focus_set(it, ELM_FOCUS_UP, sd->h_mode);
if ((!sd->h_mode && !focused) &&
(((evas_key_modifier_is_set(ev->modifiers, "Shift")) &&
(_item_multi_select_up(sd)))
|| (_item_single_select_up(sd))))
@ -275,7 +345,10 @@ _elm_list_smart_event(Eo *obj, void *_pd, va_list *list)
else if ((!strcmp(ev->keyname, "Down")) ||
((!strcmp(ev->keyname, "KP_Down")) && !ev->string))
{
if ((!sd->h_mode) &&
it = (Elm_List_Item *)elm_list_selected_item_get(obj);
Eina_Bool focused = _elm_list_item_focus_set(it, ELM_FOCUS_DOWN, sd->h_mode);
if ((!sd->h_mode && !focused) &&
(((evas_key_modifier_is_set(ev->modifiers, "Shift")) &&
(_item_multi_select_down(sd)))
|| (_item_single_select_down(sd))))
@ -915,6 +988,9 @@ _item_select(Elm_List_Item *it)
if (sd->select_mode == ELM_OBJECT_SELECT_MODE_ALWAYS) goto call;
return;
}
_elm_list_item_focus_set(it, ELM_FOCUS_PREVIOUS, sd->h_mode);
it->selected = EINA_TRUE;
sd->selected = eina_list_append(sd->selected, it);
@ -943,6 +1019,12 @@ _item_unselect(Elm_List_Item *it)
evas_object_ref(obj);
_elm_list_walk(sd);
if (it->sd->focus_on_selection_enabled)
{
if (it->icon) elm_object_focus_set(it->icon, EINA_FALSE);
if (it->end) elm_object_focus_set(it->end, EINA_FALSE);
}
edje_object_signal_emit(VIEW(it), "elm,state,unselected", "elm");
evas_object_smart_callback_call(obj, SIG_UNHIGHLIGHTED, it);
stacking = edje_object_data_get(VIEW(it), "stacking");
@ -2545,6 +2627,39 @@ _at_xy_item_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
*ret = NULL;
}
EAPI void
elm_list_focus_on_selection_set(Evas_Object *obj,
Eina_Bool enabled)
{
ELM_LIST_CHECK(obj);
eo_do(obj, elm_obj_list_focus_on_selection_set(enabled));
}
static void
_focus_on_selection_set(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
{
Eina_Bool enabled = va_arg(*list, int);
Elm_List_Smart_Data *sd = _pd;
sd->focus_on_selection_enabled = !!enabled;
}
EAPI Eina_Bool
elm_list_focus_on_selection_get(const Evas_Object *obj)
{
ELM_LIST_CHECK(obj) EINA_FALSE;
Eina_Bool ret = EINA_FALSE;
eo_do((Eo *) obj, elm_obj_list_focus_on_selection_get(&ret));
return ret;
}
static void
_focus_on_selection_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
{
Eina_Bool *ret = va_arg(*list, Eina_Bool *);
Elm_List_Smart_Data *sd = _pd;
*ret = sd->focus_on_selection_enabled;
}
static void
_class_constructor(Eo_Class *klass)
{
@ -2593,6 +2708,8 @@ _class_constructor(Eo_Class *klass)
EO_OP_FUNC(ELM_OBJ_LIST_ID(ELM_OBJ_LIST_SUB_ID_FIRST_ITEM_GET), _first_item_get),
EO_OP_FUNC(ELM_OBJ_LIST_ID(ELM_OBJ_LIST_SUB_ID_LAST_ITEM_GET), _last_item_get),
EO_OP_FUNC(ELM_OBJ_LIST_ID(ELM_OBJ_LIST_SUB_ID_AT_XY_ITEM_GET), _at_xy_item_get),
EO_OP_FUNC(ELM_OBJ_LIST_ID(ELM_OBJ_LIST_SUB_ID_FOCUS_ON_SELECTION_SET), _focus_on_selection_set),
EO_OP_FUNC(ELM_OBJ_LIST_ID(ELM_OBJ_LIST_SUB_ID_FOCUS_ON_SELECTION_GET), _focus_on_selection_get),
EO_OP_FUNC_SENTINEL
};
eo_class_funcs_set(klass, func_desc);
@ -2625,6 +2742,8 @@ static const Eo_Op_Description op_desc[] = {
EO_OP_DESCRIPTION(ELM_OBJ_LIST_SUB_ID_FIRST_ITEM_GET, "Get the first item in the list."),
EO_OP_DESCRIPTION(ELM_OBJ_LIST_SUB_ID_LAST_ITEM_GET, "Get the last item in the list."),
EO_OP_DESCRIPTION(ELM_OBJ_LIST_SUB_ID_AT_XY_ITEM_GET, "Get the item that is at the x, y canvas coords."),
EO_OP_DESCRIPTION(ELM_OBJ_LIST_SUB_ID_FOCUS_ON_SELECTION_SET, "Set focus upon item's selection mode."),
EO_OP_DESCRIPTION(ELM_OBJ_LIST_SUB_ID_FOCUS_ON_SELECTION_GET, "Get focus upon item's selection mode."),
EO_OP_DESCRIPTION_SENTINEL
};

View File

@ -32,6 +32,8 @@
ELM_OBJ_LIST_SUB_ID_FIRST_ITEM_GET,
ELM_OBJ_LIST_SUB_ID_LAST_ITEM_GET,
ELM_OBJ_LIST_SUB_ID_AT_XY_ITEM_GET,
ELM_OBJ_LIST_SUB_ID_FOCUS_ON_SELECTION_SET,
ELM_OBJ_LIST_SUB_ID_FOCUS_ON_SELECTION_GET,
ELM_OBJ_LIST_SUB_ID_LAST
};
@ -371,6 +373,30 @@
*/
#define elm_obj_list_at_xy_item_get(x, y, posret, ret) ELM_OBJ_LIST_ID(ELM_OBJ_LIST_SUB_ID_AT_XY_ITEM_GET), EO_TYPECHECK(Evas_Coord, x), EO_TYPECHECK(Evas_Coord, y), EO_TYPECHECK(int *, posret), EO_TYPECHECK(Elm_Object_Item **, ret)
/**
* @def elm_obj_list_focus_on_selection_set
* @since 1.8
*
* Set focus to a first from left focusable widget upon item selection.
*
* @param[in] enabled
*
* @see elm_obj_list_focus_on_selection_get
*/
#define elm_obj_list_focus_on_selection_set(enabled) ELM_OBJ_LIST_ID(ELM_OBJ_LIST_SUB_ID_FOCUS_ON_SELECTION_SET), EO_TYPECHECK(Eina_Bool, enabled)
/**
* @def elm_obj_list_focus_on_selection_get
* @since 1.8
*
* Get whether the focus will be set to a widget on an item upon it's selection.
*
* @param[out] ret
*
* @see elm_obj_list_focus_on_selection_set
*/
#define elm_obj_list_focus_on_selection_get(ret) ELM_OBJ_LIST_ID(ELM_OBJ_LIST_SUB_ID_FOCUS_ON_SELECTION_GET), EO_TYPECHECK(Eina_Bool *, ret)
/**
* @}
*/

View File

@ -524,3 +524,30 @@ EAPI Elm_Object_Item *elm_list_last_item_get(const Evas_Object *obj)
* @ingroup List
*/
EAPI Elm_Object_Item *elm_list_at_xy_item_get(const Evas_Object *obj, Evas_Coord x, Evas_Coord y, int *posret);
/**
* Set focus upon items selection mode
*
* @param obj The genlist object
* @param enabled The tree effect status
* (EINA_TRUE = enabled, EINA_FALSE = disabled)
*
* When enabled, every selection of an item inside the genlist will automatically set focus to
* its first focusable widget from the left. This is true of course if the selection was made by
* clicking an unfocusable area in an item or selecting it with a key movement. Clicking on a
* focusable widget inside an item will couse this particular item to get focus as usual.
*
* @ingroup List
*/
EAPI void elm_list_focus_on_selection_set(Evas_Object *obj, Eina_Bool enabled);
/**
* Gets whether focus upon item's selection mode is enabled.
*
* @param obj The genlist object
* @return The tree effect status
* (EINA_TRUE = enabled, EINA_FALSE = disabled)
*
* @ingroup List
*/
EAPI Eina_Bool elm_list_focus_on_selection_get(const Evas_Object *obj);

View File

@ -131,6 +131,7 @@ struct _Elm_Genlist_Smart_Data
Ecore_Animator *tree_effect_animator;
Elm_Genlist_Item_Move_Effect_Mode move_effect_mode;
Eina_Bool focus_on_selection_enabled : 1;
Eina_Bool tree_effect_enabled : 1;
Eina_Bool auto_scroll_enabled : 1;
Eina_Bool decorate_all_mode : 1;

View File

@ -39,6 +39,7 @@ struct _Elm_List_Smart_Data
Evas_Coord x, y;
} history[ELM_LIST_SWIPE_MOVES];
Eina_Bool focus_on_selection_enabled : 1;
Eina_Bool was_selected : 1;
Eina_Bool fix_pending : 1;
Eina_Bool longpressed : 1;