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
This commit is contained in:
Marcel Hollerbach 2018-11-26 12:40:17 +01:00
parent c435cdf173
commit a444ff743f
10 changed files with 166 additions and 10 deletions

View File

@ -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<Efl.Ui.Focus.Object>;
}
}
@property root {
[[Root node for all logical subtrees.

View File

@ -937,8 +937,74 @@ 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)
{
@ -946,17 +1012,21 @@ _iterator_next(Border_Elements_Iterator *it, void **data)
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;
}

View File

@ -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;

View File

@ -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)
{

View File

@ -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;

View File

@ -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)

View File

@ -103,6 +103,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);
}
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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;}
}
}