diff --git a/data/elementary/config/default/base.src.in b/data/elementary/config/default/base.src.in index 20670544eb..79f7a1bb55 100644 --- a/data/elementary/config/default/base.src.in +++ b/data/elementary/config/default/base.src.in @@ -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"; + } + } + } } } diff --git a/data/elementary/config/mobile/base.src.in b/data/elementary/config/mobile/base.src.in index 00de42f095..9397150576 100644 --- a/data/elementary/config/mobile/base.src.in +++ b/data/elementary/config/mobile/base.src.in @@ -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"; + } + } + } } } diff --git a/data/elementary/config/standard/base.src.in b/data/elementary/config/standard/base.src.in index bf9aaf4c24..ccfa97b05e 100644 --- a/data/elementary/config/standard/base.src.in +++ b/data/elementary/config/standard/base.src.in @@ -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"; + } + } + } } } diff --git a/src/lib/elementary/efl_ui_scroller.c b/src/lib/elementary/efl_ui_scroller.c index a4f654555e..52af40be49 100644 --- a/src/lib/elementary/efl_ui_scroller.c +++ b/src/lib/elementary/efl_ui_scroller.c @@ -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 #include "elm_priv.h" @@ -27,6 +28,126 @@ 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) @@ -406,6 +527,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) @@ -434,6 +574,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, @@ -451,6 +593,7 @@ _efl_ui_scroller_efl_object_finalize(Eo *obj, efl_event_callback_add(sd->pan_obj, EFL_GFX_ENTITY_EVENT_SIZE_CHANGED, _efl_ui_scroller_pan_resized_cb, obj); + efl_event_callback_add(obj, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, _focused_element, obj); return obj; } @@ -563,6 +706,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 \ diff --git a/src/lib/elementary/efl_ui_scroller.eo b/src/lib/elementary/efl_ui_scroller.eo index bb9d998640..76447ab7d5 100644 --- a/src/lib/elementary/efl_ui_scroller.eo +++ b/src/lib/elementary/efl_ui_scroller.eo @@ -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 { diff --git a/src/lib/elementary/elm_priv.h b/src/lib/elementary/elm_priv.h index 1ac0bfed07..780b20a746 100644 --- a/src/lib/elementary/elm_priv.h +++ b/src/lib/elementary/elm_priv.h @@ -177,7 +177,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)