efl_ui_scroller: apply handling focus.
authorHosang Kim <hosang12.kim@samsung.com>
Tue, 26 Feb 2019 07:52:54 +0000 (16:52 +0900)
committerWonki Kim <wonki_.kim@samsung.com>
Fri, 8 Mar 2019 11:49:36 +0000 (20:49 +0900)
Summary: Focus manager is applied to process key events.

Test Plan: elementary_test -> efl.ui.scroller

Reviewers: SanghyeonLee, YOhoho, marcelhollerbach, bu5hm4n

Reviewed By: bu5hm4n

Subscribers: woohyun, Jaehyun_Cho, bu5hm4n, cedric, #reviewers, #committers

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D7776

data/elementary/config/default/base.src.in
data/elementary/config/mobile/base.src.in
data/elementary/config/standard/base.src.in
src/lib/elementary/efl_ui_scroller.c
src/lib/elementary/efl_ui_scroller.eo
src/lib/elementary/elm_priv.h

index 0900852..1fa9e76 100644 (file)
@@ -1,5 +1,5 @@
 group "Elm_Config" struct {
-  value "config_version" int: 131092;
+  value "config_version" int: 131093;
   value "entry_select_allow" uchar: 1;
   value "engine" string: "";
   value "vsync" uchar: 0;
@@ -3153,5 +3153,106 @@ group "Elm_Config" struct {
            }
         }
      }
+     group "Elm_Config_Bindings_Widget" struct {
+        value "name" string: "Efl.Ui.Scroller";
+        group "key_bindings" list {
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Left";
+              value "action" string: "move";
+              value "params" string: "left";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Left";
+              value "action" string: "move";
+              value "params" string: "left";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Right";
+              value "action" string: "move";
+              value "params" string: "right";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Right";
+              value "action" string: "move";
+              value "params" string: "right";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Up";
+              value "action" string: "move";
+              value "params" string: "up";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Up";
+              value "action" string: "move";
+              value "params" string: "up";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Down";
+              value "action" string: "move";
+              value "params" string: "down";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Down";
+              value "action" string: "move";
+              value "params" string: "down";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Home";
+              value "action" string: "move";
+              value "params" string: "first";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Home";
+              value "action" string: "move";
+              value "params" string: "first";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "End";
+              value "action" string: "move";
+              value "params" string: "last";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_End";
+              value "action" string: "move";
+              value "params" string: "last";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Prior";
+              value "action" string: "move";
+              value "params" string: "prior";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Prior";
+              value "action" string: "move";
+              value "params" string: "prior";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Next";
+              value "action" string: "move";
+              value "params" string: "next";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Next";
+              value "action" string: "move";
+              value "params" string: "next";
+           }
+               }
+        }
   }
 }
index 00de42f..9397150 100644 (file)
@@ -1,5 +1,5 @@
 group "Elm_Config" struct {
-  value "config_version" int: 131092;
+  value "config_version" int: 131093;
   value "entry_select_allow" uchar: 1;
   value "engine" string: "";
   value "vsync" uchar: 0;
@@ -3139,5 +3139,106 @@ group "Elm_Config" struct {
            }
         }
      }
+     group "Elm_Config_Bindings_Widget" struct {
+        value "name" string: "Efl.Ui.Scroller";
+        group "key_bindings" list {
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Left";
+              value "action" string: "move";
+              value "params" string: "left";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Left";
+              value "action" string: "move";
+              value "params" string: "left";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Right";
+              value "action" string: "move";
+              value "params" string: "right";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Right";
+              value "action" string: "move";
+              value "params" string: "right";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Up";
+              value "action" string: "move";
+              value "params" string: "up";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Up";
+              value "action" string: "move";
+              value "params" string: "up";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Down";
+              value "action" string: "move";
+              value "params" string: "down";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Down";
+              value "action" string: "move";
+              value "params" string: "down";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Home";
+              value "action" string: "move";
+              value "params" string: "first";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Home";
+              value "action" string: "move";
+              value "params" string: "first";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "End";
+              value "action" string: "move";
+              value "params" string: "last";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_End";
+              value "action" string: "move";
+              value "params" string: "last";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Prior";
+              value "action" string: "move";
+              value "params" string: "prior";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Prior";
+              value "action" string: "move";
+              value "params" string: "prior";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Next";
+              value "action" string: "move";
+              value "params" string: "next";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Next";
+              value "action" string: "move";
+              value "params" string: "next";
+           }
+               }
+        }
   }
 }
index d9ab4a4..24f0ddb 100644 (file)
@@ -1,5 +1,5 @@
 group "Elm_Config" struct {
-  value "config_version" int: 131092;
+  value "config_version" int: 131093;
   value "entry_select_allow" uchar: 1;
   value "engine" string: "";
   value "vsync" uchar: 0;
@@ -3136,5 +3136,106 @@ group "Elm_Config" struct {
            }
         }
      }
+     group "Elm_Config_Bindings_Widget" struct {
+        value "name" string: "Efl.Ui.Scroller";
+        group "key_bindings" list {
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Left";
+              value "action" string: "move";
+              value "params" string: "left";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Left";
+              value "action" string: "move";
+              value "params" string: "left";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Right";
+              value "action" string: "move";
+              value "params" string: "right";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Right";
+              value "action" string: "move";
+              value "params" string: "right";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Up";
+              value "action" string: "move";
+              value "params" string: "up";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Up";
+              value "action" string: "move";
+              value "params" string: "up";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Down";
+              value "action" string: "move";
+              value "params" string: "down";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Down";
+              value "action" string: "move";
+              value "params" string: "down";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Home";
+              value "action" string: "move";
+              value "params" string: "first";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Home";
+              value "action" string: "move";
+              value "params" string: "first";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "End";
+              value "action" string: "move";
+              value "params" string: "last";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_End";
+              value "action" string: "move";
+              value "params" string: "last";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Prior";
+              value "action" string: "move";
+              value "params" string: "prior";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Prior";
+              value "action" string: "move";
+              value "params" string: "prior";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "Next";
+              value "action" string: "move";
+              value "params" string: "next";
+           }
+           group "Elm_Config_Binding_Key" struct {
+              value "context" int: 0;
+              value "key" string: "KP_Next";
+              value "action" string: "move";
+              value "params" string: "next";
+           }
+               }
+        }
   }
 }
index 6ac1e66..abb908a 100644 (file)
@@ -5,6 +5,7 @@
 #define ELM_LAYOUT_PROTECTED
 #define EFL_UI_SCROLL_MANAGER_PROTECTED
 #define EFL_UI_SCROLLBAR_PROTECTED
+#define EFL_UI_WIDGET_FOCUS_MANAGER_PROTECTED
 
 #include <Elementary.h>
 #include "elm_priv.h"
            o, evas_object_type_get(o));               \
        return __VA_ARGS__;                                    \
     }
+
+static Eina_Bool _key_action_move(Evas_Object *obj, const char *params);
+
+static const Elm_Action key_actions[] = {
+   {"move", _key_action_move},
+   {NULL, NULL}
+};
+
+static Eina_Bool
+_key_action_move(Eo *obj, const char *params)
+{
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(obj, sd, EINA_FALSE);
+
+   const char *dir = params;
+   Efl_Ui_Focus_Direction focus_dir = 0;
+   Efl_Ui_Focus_Object *focused, *next_target;
+   Eina_Rect focused_geom, viewport;
+   Eina_Position2D pos;
+   Eina_Size2D max;
+   Eina_Bool scroller_adjustment = EINA_FALSE;
+
+   pos = efl_ui_scrollable_content_pos_get(obj);
+   viewport = efl_ui_scrollable_viewport_geometry_get(obj);
+   max = efl_gfx_entity_size_get(sd->content);
+   if (!strcmp(dir, "prior"))
+     focus_dir = EFL_UI_FOCUS_DIRECTION_PREVIOUS;
+   else if (!strcmp(dir, "next"))
+     focus_dir = EFL_UI_FOCUS_DIRECTION_NEXT;
+   else if (!strcmp(dir, "left"))
+     focus_dir = EFL_UI_FOCUS_DIRECTION_LEFT;
+   else if (!strcmp(dir, "right"))
+     focus_dir = EFL_UI_FOCUS_DIRECTION_RIGHT;
+   else if (!strcmp(dir, "up"))
+     focus_dir = EFL_UI_FOCUS_DIRECTION_UP;
+   else if (!strcmp(dir, "down"))
+     focus_dir = EFL_UI_FOCUS_DIRECTION_DOWN;
+   else return EINA_FALSE;
+
+   focused = efl_ui_focus_manager_focus_get(obj);
+   next_target = efl_ui_focus_manager_request_move(obj, focus_dir, focused, EINA_FALSE);
+
+   //logical movement is handled by focus directly
+   if (focused &&
+       (focus_dir == EFL_UI_FOCUS_DIRECTION_NEXT ||
+        focus_dir == EFL_UI_FOCUS_DIRECTION_PREVIOUS))
+     return EINA_FALSE;
+   //check if a object that is focused is lapping out of the viewport
+   // if this is the case, and the object is lapping out of the viewport in
+   // the direction we want to move, then move the scroller
+   if (focused)
+     {
+        Eina_Rectangle_Outside relative;
+
+        focused_geom = efl_gfx_entity_geometry_get(focused);
+
+        relative = eina_rectangle_outside_position(&viewport.rect, &focused_geom.rect);
+
+        //now precisly check if the direction is also lapping out
+        if ((focus_dir == EFL_UI_FOCUS_DIRECTION_UP && (relative & EINA_RECTANGLE_OUTSIDE_TOP)) ||
+            (focus_dir == EFL_UI_FOCUS_DIRECTION_LEFT && (relative & EINA_RECTANGLE_OUTSIDE_LEFT)) ||
+            (focus_dir == EFL_UI_FOCUS_DIRECTION_DOWN && (relative & EINA_RECTANGLE_OUTSIDE_BOTTOM)) ||
+            (focus_dir == EFL_UI_FOCUS_DIRECTION_RIGHT && (relative & EINA_RECTANGLE_OUTSIDE_RIGHT)))
+          {
+             scroller_adjustment = EINA_TRUE;
+          }
+     }
+   //check if there is a next target in the direction where we want to move
+   //if not, and the scroller is not at its max in that relation,
+   //then move the scroller instead of the focus
+   if (!next_target)
+     {
+        if ((focus_dir == EFL_UI_FOCUS_DIRECTION_UP && (pos.y != 0)) ||
+            (focus_dir == EFL_UI_FOCUS_DIRECTION_LEFT && (pos.x != 0)) ||
+            (focus_dir == EFL_UI_FOCUS_DIRECTION_DOWN && (pos.y != max.h)) ||
+            (focus_dir == EFL_UI_FOCUS_DIRECTION_RIGHT && (pos.x != max.w)))
+          {
+             scroller_adjustment = EINA_TRUE;
+          }
+     }
+   if (!scroller_adjustment)
+     return EINA_FALSE;
+
+   Eina_Position2D step = efl_ui_scrollable_step_size_get(obj);
+
+   if (!strcmp(dir, "left"))
+     {
+        if (pos.x <= 0) return EINA_FALSE;
+        pos.x -= step.x;
+     }
+   else if (!strcmp(dir, "right"))
+     {
+        if (pos.x >= (max.w - viewport.w)) return EINA_FALSE;
+        pos.x += step.x;
+     }
+   else if (!strcmp(dir, "up"))
+     {
+        if (pos.y <= 0) return EINA_FALSE;
+        pos.y -= step.y;
+     }
+   else if (!strcmp(dir, "down"))
+     {
+        if (pos.y >= (max.h - viewport.h)) return EINA_FALSE;
+        pos.y += step.y;
+     }
+   else if (!strcmp(dir, "first"))
+     {
+        pos.y = 0;
+     }
+   else if (!strcmp(dir, "last"))
+     {
+        pos.y = max.h - viewport.h;
+     }
+   else return EINA_FALSE;
+
+   efl_ui_scrollable_scroll(obj, EINA_RECT(pos.x, pos.y, viewport.w, viewport.h), EINA_FALSE);
+
+   return EINA_TRUE;
+}
+
+
 static void
 _efl_ui_scroller_content_del_cb(void *data,
                                 const Efl_Event *event EINA_UNUSED)
@@ -421,6 +542,25 @@ _efl_ui_scroller_size_hint_changed_cb(void *data, const Efl_Event *ev EINA_UNUSE
    elm_layout_sizing_eval(data);
 }
 
+static void
+_focused_element(void *data, const Efl_Event *event)
+{
+   Eina_Rect geom;
+   Efl_Ui_Focus_Object *obj = data;
+   Efl_Ui_Focus_Object *focus = efl_ui_focus_manager_focus_get(event->object);
+   Eina_Position2D pos, pan;
+
+   if (!focus) return;
+
+   geom = efl_ui_focus_object_focus_geometry_get(focus);
+   pos = efl_gfx_entity_position_get(obj);
+   pan = efl_ui_scrollable_content_pos_get(obj);
+   geom.x += pan.x - pos.x;
+   geom.y += pan.y - pos.y;
+
+   efl_ui_scrollable_scroll(obj, geom, EINA_TRUE);
+}
+
 EOLIAN static Eo *
 _efl_ui_scroller_efl_object_constructor(Eo *obj,
                                         Efl_Ui_Scroller_Data *sd EINA_UNUSED)
@@ -449,6 +589,8 @@ _efl_ui_scroller_efl_object_finalize(Eo *obj,
    efl_ui_scroll_manager_pan_set(sd->smanager, sd->pan_obj);
    edje_object_part_swallow(wd->resize_obj, "efl.content", sd->pan_obj);
 
+   elm_widget_can_focus_set(obj, EINA_TRUE);
+
    _scroll_edje_object_attach(obj);
 
    efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_SIZE_CHANGED,
@@ -471,6 +613,7 @@ _efl_ui_scroller_efl_object_finalize(Eo *obj,
    efl_ui_drag_page_set(efl_part(wd->resize_obj, "efl.dragable.vbar"), 0.0, 0.033);
    //
 
+   efl_event_callback_add(obj, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, _focused_element, obj);
    return obj;
 }
 
@@ -583,6 +726,28 @@ _efl_ui_scroller_efl_ui_scrollable_interactive_match_content_set(Eo *obj EINA_UN
    elm_layout_sizing_eval(obj);
 }
 
+EOLIAN static Eina_Bool
+_efl_ui_scroller_efl_ui_widget_focus_state_apply(Eo *obj, Efl_Ui_Scroller_Data *pd EINA_UNUSED, Efl_Ui_Widget_Focus_State current_state, Efl_Ui_Widget_Focus_State *configured_state, Efl_Ui_Widget *redirect EINA_UNUSED)
+{
+   //undepended from logical or not we always reigster as full with ourself as redirect
+   configured_state->logical = EINA_TRUE;
+   return efl_ui_widget_focus_state_apply(efl_super(obj, MY_CLASS), current_state, configured_state, obj);
+}
+
+EOLIAN static Efl_Ui_Focus_Manager*
+_efl_ui_scroller_efl_ui_widget_focus_manager_focus_manager_create(Eo *obj, Efl_Ui_Scroller_Data *pd EINA_UNUSED, Efl_Ui_Focus_Object *root)
+{
+   Efl_Ui_Focus_Manager *manager;
+   manager = efl_add(EFL_UI_FOCUS_MANAGER_ROOT_FOCUS_CLASS, obj,
+                     efl_ui_focus_manager_root_set(efl_added, root));
+
+   return manager;
+}
+
+/* Standard widget overrides */
+
+ELM_WIDGET_KEY_DOWN_DEFAULT_IMPLEMENT(efl_ui_scroller, Efl_Ui_Scroller_Data)
+
 /* Internal EO APIs and hidden overrides */
 
 #define EFL_UI_SCROLLER_EXTRA_OPS \
index bb9d998..76447ab 100644 (file)
@@ -1,6 +1,8 @@
 class @beta Efl.Ui.Scroller extends Efl.Ui.Layout implements
                        Efl.Ui.Scrollable_Interactive,
                        Efl.Ui.Scrollbar,
+                       Efl.Ui.Focus.Manager_Sub,
+                       Efl.Ui.Widget_Focus_Manager,
                        Efl.Content
 {
    [[Efl ui scroller class]]
@@ -11,6 +13,9 @@ class @beta Efl.Ui.Scroller extends Efl.Ui.Layout implements
       Efl.Content.content { get; set; }
       Efl.Content.content_unset;
       Efl.Ui.Widget.theme_apply;
+      Efl.Ui.Widget.focus_state_apply;
+      Efl.Ui.Widget.widget_event;
+      Efl.Ui.Widget_Focus_Manager.focus_manager_create;
       Efl.Ui.Scrollable_Interactive.match_content { set; }
    }
    composite {
index d41b9e6..e0f2d5d 100644 (file)
@@ -180,7 +180,7 @@ struct _Efl_Ui_Theme_Data
  * the users config doesn't need to be wiped - simply new values need
  * to be put in
  */
-# define ELM_CONFIG_FILE_GENERATION 0x0014
+# define ELM_CONFIG_FILE_GENERATION 0x0015
 # define ELM_CONFIG_VERSION_EPOCH_OFFSET 16
 # define ELM_CONFIG_VERSION         ((ELM_CONFIG_EPOCH << ELM_CONFIG_VERSION_EPOCH_OFFSET) | \
                                      ELM_CONFIG_FILE_GENERATION)