From ab4e10f1b4b148eb994da20943df4a061349c32c Mon Sep 17 00:00:00 2001 From: Marcel Hollerbach Date: Mon, 26 Nov 2018 12:40:17 +0100 Subject: [PATCH] elm_interface_scrollable: add support for *jumping* into a scroller Until recently we have been only registering the border elements of the graph, (so only the elements that don't have a neighboor). However this lead to the situation that a scroller that is scrolled into the middle (so not the x nor the y axis is scrolled to the max), is not accessable. Now, we register all elements that have a neighboor in the outside. The patch in the test suite is required in order to provide the correct geometry to the implementation of efl_ui_focus_manager_sub. Differential Revision: https://phab.enlightenment.org/D7360 --- src/lib/elementary/efl_ui_focus_manager.eo | 15 +++ src/lib/elementary/efl_ui_focus_manager_calc.c | 103 +++++++++++++++++++-- src/lib/elementary/efl_ui_focus_manager_calc.eo | 1 + .../elementary/efl_ui_focus_manager_root_focus.c | 9 ++ .../elementary/efl_ui_focus_manager_root_focus.eo | 1 + src/lib/elementary/efl_ui_focus_manager_sub.c | 2 +- src/lib/elementary/elm_interface_scrollable.c | 4 + src/tests/elementary/elm_test_focus.c | 33 +++++++ src/tests/elementary/elm_test_focus_sub.c | 5 + src/tests/elementary/focus_test_sub_main.eo | 3 +- 10 files changed, 166 insertions(+), 10 deletions(-) diff --git a/src/lib/elementary/efl_ui_focus_manager.eo b/src/lib/elementary/efl_ui_focus_manager.eo index 767c0b6..3561471 100644 --- a/src/lib/elementary/efl_ui_focus_manager.eo +++ b/src/lib/elementary/efl_ui_focus_manager.eo @@ -1,4 +1,5 @@ import efl_ui; +import eina_types; struct Efl.Ui.Focus.Relations { [[Structure holding the graph of relations between focussable objects. @@ -90,6 +91,20 @@ interface Efl.Ui.Focus.Manager { over the border objects.]] } } + @property viewport_elements { + [[The list of elements which are at the border of the viewport. + + This means one of the relations right,left or down,up are not set. + This call flushes all changes. See @Efl.Ui.Focus.Manager.move + ]] + get {} + keys { + viewport : Eina.Rect; + } + values { + viewport_elements : iterator; + } + } @property root { [[Root node for all logical subtrees. diff --git a/src/lib/elementary/efl_ui_focus_manager_calc.c b/src/lib/elementary/efl_ui_focus_manager_calc.c index e4d0332..d6b5569 100644 --- a/src/lib/elementary/efl_ui_focus_manager_calc.c +++ b/src/lib/elementary/efl_ui_focus_manager_calc.c @@ -937,26 +937,96 @@ typedef struct { Eina_Iterator iterator; Eina_Iterator *real_iterator; Efl_Ui_Focus_Manager *object; + Eina_Each_Cb filter_cb; + Eina_Rect viewport; + Eina_Bool use_viewport; } Border_Elements_Iterator; static Eina_Bool +_border_filter_cb(Node *node, Border_Elements_Iterator *pd EINA_UNUSED) +{ + for(int i = EFL_UI_FOCUS_DIRECTION_UP ;i < EFL_UI_FOCUS_DIRECTION_LAST; i++) + { + if (!DIRECTION_ACCESS(node, i).one_direction) + { + return EINA_TRUE; + } + } + return EINA_FALSE; +} + +static Eina_Bool +eina_rectangle_real_inside(Eina_Rect rect, Eina_Rect geom) +{ + int min_x, max_x, min_y, max_y; + + min_x = geom.rect.x; + min_y = geom.rect.y; + max_x = eina_rectangle_max_x(&geom.rect); + max_y = eina_rectangle_max_y(&geom.rect); + + Eina_Bool inside = eina_rectangle_coords_inside(&rect.rect, min_x, min_y) && + eina_rectangle_coords_inside(&rect.rect, min_x, max_y) && + eina_rectangle_coords_inside(&rect.rect, max_x, min_y) && + eina_rectangle_coords_inside(&rect.rect, max_x, max_y); + + return inside; +} + +static Eina_Bool +_viewport_filter_cb(Node *node, Border_Elements_Iterator *pd) +{ + Node *partner; + Eina_Rect geom; + + if (node->type == NODE_TYPE_ONLY_LOGICAL) return EINA_FALSE; + + geom = efl_ui_focus_object_focus_geometry_get(node->focusable); + + if (eina_rectangle_real_inside(pd->viewport, geom)) + { + for (int i = 0; i < 4; ++i) + { + Eina_List *n, *lst = G(node).directions[i].one_direction; + + if (!lst) + return EINA_TRUE; + + EINA_LIST_FOREACH(lst, n, partner) + { + Eina_Rect partner_geom; + partner_geom = efl_ui_focus_object_focus_geometry_get(partner->focusable); + if (!eina_rectangle_real_inside(pd->viewport, partner_geom)) + return EINA_TRUE; + } + } + } + return EINA_FALSE; +} + + +static Eina_Bool _iterator_next(Border_Elements_Iterator *it, void **data) { Node *node; EINA_ITERATOR_FOREACH(it->real_iterator, node) { + Eina_Bool use = EINA_FALSE; if (node->type == NODE_TYPE_ONLY_LOGICAL) continue; - for(int i = EFL_UI_FOCUS_DIRECTION_UP ;i < EFL_UI_FOCUS_DIRECTION_LAST; i++) + if (!it->use_viewport) + use = _border_filter_cb(node, it); + else + use = _viewport_filter_cb(node, it); + + if (use) { - if (!DIRECTION_ACCESS(node, i).one_direction) - { - *data = node->focusable; - return EINA_TRUE; - } + *data = node->focusable; + return EINA_TRUE; } } + return EINA_FALSE; } @@ -987,8 +1057,8 @@ _prepare_node(Node *root) } } -EOLIAN static Eina_Iterator* -_efl_ui_focus_manager_calc_efl_ui_focus_manager_border_elements_get(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd) +static Border_Elements_Iterator* +_elements_iterator_new(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd) { Border_Elements_Iterator *it; @@ -1009,6 +1079,23 @@ _efl_ui_focus_manager_calc_efl_ui_focus_manager_border_elements_get(const Eo *ob it->iterator.free = FUNC_ITERATOR_FREE(_iterator_free); it->object = (Eo *)obj; + return it; +} + +EOLIAN static Eina_Iterator* +_efl_ui_focus_manager_calc_efl_ui_focus_manager_border_elements_get(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd) +{ + return (Eina_Iterator*) _elements_iterator_new(obj, pd); +} + +EOLIAN static Eina_Iterator* +_efl_ui_focus_manager_calc_efl_ui_focus_manager_viewport_elements_get(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Eina_Rect viewport) +{ + Border_Elements_Iterator *it = _elements_iterator_new(obj, pd); + + it->use_viewport = EINA_TRUE; + it->viewport = viewport; + return (Eina_Iterator*) it; } diff --git a/src/lib/elementary/efl_ui_focus_manager_calc.eo b/src/lib/elementary/efl_ui_focus_manager_calc.eo index ba4addf..8bf3096 100644 --- a/src/lib/elementary/efl_ui_focus_manager_calc.eo +++ b/src/lib/elementary/efl_ui_focus_manager_calc.eo @@ -95,6 +95,7 @@ class Efl.Ui.Focus.Manager_Calc (Efl.Object, Efl.Ui.Focus.Manager) { Efl.Ui.Focus.Manager.manager_focus {get; set;} Efl.Ui.Focus.Manager.redirect {set; get;} Efl.Ui.Focus.Manager.border_elements {get;} + Efl.Ui.Focus.Manager.viewport_elements {get;} Efl.Ui.Focus.Manager.root {set; get;} Efl.Ui.Focus.Manager.request_subchild; Efl.Ui.Focus.Manager.fetch; diff --git a/src/lib/elementary/efl_ui_focus_manager_root_focus.c b/src/lib/elementary/efl_ui_focus_manager_root_focus.c index 3070a6f..0df8b5e 100644 --- a/src/lib/elementary/efl_ui_focus_manager_root_focus.c +++ b/src/lib/elementary/efl_ui_focus_manager_root_focus.c @@ -141,6 +141,15 @@ _efl_ui_focus_manager_root_focus_efl_ui_focus_manager_border_elements_get(const return efl_ui_focus_manager_border_elements_get(efl_super(obj, MY_CLASS)); } +EOLIAN static Eina_Iterator * +_efl_ui_focus_manager_root_focus_efl_ui_focus_manager_viewport_elements_get(const Eo *obj, Efl_Ui_Focus_Manager_Root_Focus_Data *pd, Eina_Rect viewport) +{ + if (pd->rect_registered) + return eina_list_iterator_new(pd->iterator_list); + + return efl_ui_focus_manager_border_elements_get(efl_super(obj, MY_CLASS)); +} + EOLIAN static Efl_Ui_Focus_Object* _efl_ui_focus_manager_root_focus_efl_ui_focus_manager_request_move(Eo *obj, Efl_Ui_Focus_Manager_Root_Focus_Data *pd, Efl_Ui_Focus_Direction direction, Efl_Ui_Focus_Object *child, Eina_Bool logical) { diff --git a/src/lib/elementary/efl_ui_focus_manager_root_focus.eo b/src/lib/elementary/efl_ui_focus_manager_root_focus.eo index 81bd312..3f89751 100644 --- a/src/lib/elementary/efl_ui_focus_manager_root_focus.eo +++ b/src/lib/elementary/efl_ui_focus_manager_root_focus.eo @@ -20,6 +20,7 @@ class Efl.Ui.Focus.Manager_Root_Focus(Efl.Ui.Focus.Manager_Calc) { Efl.Ui.Focus.Manager.fetch; Efl.Ui.Focus.Manager.logical_end; Efl.Ui.Focus.Manager.border_elements {get;} + Efl.Ui.Focus.Manager.viewport_elements {get;} Efl.Ui.Focus.Manager.request_move; Efl.Ui.Focus.Manager.move; Efl.Object.constructor; diff --git a/src/lib/elementary/efl_ui_focus_manager_sub.c b/src/lib/elementary/efl_ui_focus_manager_sub.c index 90ac4f7..7fb1383 100644 --- a/src/lib/elementary/efl_ui_focus_manager_sub.c +++ b/src/lib/elementary/efl_ui_focus_manager_sub.c @@ -53,7 +53,7 @@ _border_flush(Eo *obj, Efl_Ui_Focus_Manager_Sub_Data *pd) manager = efl_ui_focus_object_focus_manager_get(obj); logical = obj; - borders = efl_ui_focus_manager_border_elements_get(obj); + borders = efl_ui_focus_manager_viewport_elements_get(obj, efl_gfx_entity_geometry_get(obj)); selection = NULL; EINA_ITERATOR_FOREACH(borders, node) diff --git a/src/lib/elementary/elm_interface_scrollable.c b/src/lib/elementary/elm_interface_scrollable.c index b614006..2210831 100644 --- a/src/lib/elementary/elm_interface_scrollable.c +++ b/src/lib/elementary/elm_interface_scrollable.c @@ -113,6 +113,10 @@ _elm_pan_update(Elm_Pan_Smart_Data *psd) efl_ui_focus_manager_dirty_logic_freeze(manager); evas_object_move(psd->content, psd->x - psd->px, psd->y - psd->py); efl_ui_focus_manager_dirty_logic_unfreeze(manager); + //XXX: hack, right now there is no api in efl_ui_focus_manager_sub.eo to mark it dirty + // If we have moved the content, then emit this event, in order to ensure that the focus_manager_sub + // logic tries to fetch the viewport again + efl_event_callback_call(manager, EFL_UI_FOCUS_MANAGER_EVENT_COORDS_DIRTY, NULL); } } diff --git a/src/tests/elementary/elm_test_focus.c b/src/tests/elementary/elm_test_focus.c index 7dde8e4..e9bc5a9 100644 --- a/src/tests/elementary/elm_test_focus.c +++ b/src/tests/elementary/elm_test_focus.c @@ -1074,6 +1074,38 @@ EFL_START_TEST(test_events_child_focus) } EFL_END_TEST + +EFL_START_TEST(viewport_check) +{ + Efl_Ui_Focus_Manager *m; + Efl_Ui_Focus_Object *middle, *east, *west, *north, *south, *root; + Eina_List *list = NULL; + Eina_Iterator *iter; + Efl_Ui_Focus_Object *obj; + + elm_focus_test_setup_cross(&middle, &south, &north, &east, &west); + + m = elm_focus_test_manager_new(&root); + efl_ui_focus_manager_calc_register(m, middle, root, NULL); + efl_ui_focus_manager_calc_register(m, south, root, NULL); + efl_ui_focus_manager_calc_register(m, north, root, NULL); + efl_ui_focus_manager_calc_register(m, east, root, NULL); + efl_ui_focus_manager_calc_register(m, west, root, NULL); + + iter = efl_ui_focus_manager_viewport_elements_get(m, EINA_RECT(80, 0, 100, 100)); + + EINA_ITERATOR_FOREACH(iter, obj) + { + list = eina_list_append(list, obj); + } + + eina_iterator_free(iter); + + ck_assert(eina_list_count(list) == 1); + ck_assert_ptr_eq(eina_list_data_get(list), east); +} +EFL_END_TEST + void elm_test_focus(TCase *tc) { tcase_add_test(tc, focus_register_twice); @@ -1105,4 +1137,5 @@ void elm_test_focus(TCase *tc) tcase_add_test(tc, test_request_move); tcase_add_test(tc, redirect_unregister_entrypoint); tcase_add_test(tc, test_events_child_focus); + tcase_add_test(tc, viewport_check); } diff --git a/src/tests/elementary/elm_test_focus_sub.c b/src/tests/elementary/elm_test_focus_sub.c index 5454ecd..9fdddbd 100644 --- a/src/tests/elementary/elm_test_focus_sub.c +++ b/src/tests/elementary/elm_test_focus_sub.c @@ -5,6 +5,11 @@ typedef struct { } Focus_Test_Sub_Main_Data; +EOLIAN static Eina_Rect +_focus_test_sub_main_efl_gfx_entity_geometry_get(const Eo *obj, Focus_Test_Sub_Main_Data *pd) +{ + return EINA_RECT(-10, -10, 40, 40); +} EOLIAN static Eina_Rect _focus_test_sub_main_efl_ui_focus_object_focus_geometry_get(const Eo *obj EINA_UNUSED, Focus_Test_Sub_Main_Data *pd EINA_UNUSED) diff --git a/src/tests/elementary/focus_test_sub_main.eo b/src/tests/elementary/focus_test_sub_main.eo index d259d38..9ac058f 100644 --- a/src/tests/elementary/focus_test_sub_main.eo +++ b/src/tests/elementary/focus_test_sub_main.eo @@ -1,10 +1,11 @@ class Focus.Test.Sub.Main extends Efl.Object - implements Efl.Ui.Focus.Object, Efl.Ui.Focus.Manager_Sub + implements Efl.Ui.Focus.Object, Efl.Ui.Focus.Manager_Sub, Efl.Gfx.Entity { implements { Efl.Ui.Focus.Object.focus_manager { get; } Efl.Ui.Focus.Object.focus_parent { get; } Efl.Ui.Focus.Object.focus_geometry { get; } + Efl.Gfx.Entity.geometry {get;} } } -- 2.7.4