elementary: Focus improvement for elm_genlist and elm_list
authorYossi Kantor <yossi.kantor@samsung.com>
Wed, 20 Feb 2013 10:00:49 +0000 (12:00 +0200)
committerDaniel Zaoui <daniel.zaoui@samsung.com>
Sun, 12 May 2013 12:32:33 +0000 (15:32 +0300)
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.

12 files changed:
src/bin/test.c
src/bin/test_genlist.c
src/bin/test_list.c
src/lib/elm_gen_common.h
src/lib/elm_genlist.c
src/lib/elm_genlist_eo.h
src/lib/elm_genlist_legacy.h
src/lib/elm_list.c
src/lib/elm_list_eo.h
src/lib/elm_list_legacy.h
src/lib/elm_widget_genlist.h
src/lib/elm_widget_list.h

index 77de0bd..6390dad 100644 (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);
index 943806b..329fa0f 100644 (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[] = {
index eef6b52..3746ccb 100644 (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__,
index 568e1fc..ba22380 100644 (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
    {
index 4f5a737..bc921f4 100644 (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);
      }
 
@@ -2358,6 +2373,50 @@ _item_single_select_down(Elm_Genlist_Smart_Data *sd)
 }
 
 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)
 {
    Evas_Object *src = va_arg(*list, Evas_Object *);
@@ -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
 };
 
index ec4bffa..42079ca 100644 (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
 };
 
@@ -511,6 +513,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)
+/**
  * @}
  */
 
index 5f76bd2..41db846 100644 (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);
index b9c9325..ba31f20 100644 (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
 };
 
index e443ba1..91bbf76 100644 (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
 };
 
 #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)
+
+/**
  * @}
  */
index 98aac27..11bbb13 100644 (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);
index 075ea6f..4d450eb 100644 (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;
index 628de21..bc72908 100644 (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;