efl_ui: introduce a focus manager and sub manager object

The Efl.Ui.Focus.Manager abstracts the creation of a localization graph
and a logical tree. The localization graph is used to find a object
right left up or down of a given object. The logical tree is used to
iterate throuw the containers which are used to build a ui.

Those managers can be used bound to some layer in the ui, so for
example the window is a layer, the content of a scroller is a layer.
With those layers, we can make sure that movements of a scroller for
example just means that this graph of objects in the scroller needs to
be recalculated, and not the complete ui.

The advantage of having this to layer bound datastructures is that you
can easily debug those graphs, since the complete layer of this
managerobject can be calculated completly.
This commit is contained in:
Marcel Hollerbach 2016-10-20 22:59:05 +02:00
parent 2cd1326c8d
commit 01f46ae205
17 changed files with 2220 additions and 1 deletions

View File

@ -118,6 +118,10 @@ elm_public_eolian_files = \
lib/elementary/efl_config_global.eo \
lib/elementary/efl_ui_clock.eo \
lib/elementary/efl_ui_image_factory.eo \
lib/elementary/efl_ui_focus_manager.eo \
lib/elementary/efl_ui_focus_manager_sub.eo \
lib/elementary/efl_ui_focus_object.eo \
lib/elementary/efl_ui_focus_user.eo \
$(NULL)
# Private classes (not exposed or shipped)
@ -669,6 +673,9 @@ lib_elementary_libelementary_la_SOURCES = \
lib/elementary/efl_ui_clock.c \
lib/elementary/efl_ui_clock_private.h \
lib/elementary/efl_ui_image_factory.c \
lib/elementary/efl_ui_focus_manager.c \
lib/elementary/efl_ui_focus_manager_sub.c \
lib/elementary/efl_ui_focus_object.c \
$(NULL)
@ -1332,7 +1339,14 @@ tests_elementary_elm_suite_SOURCES = \
tests/elementary/elm_code_test_widget.c \
tests/elementary/elm_code_test_widget_text.c \
tests/elementary/elm_code_test_widget_selection.c \
tests/elementary/elm_code_test_widget_undo.c
tests/elementary/elm_code_test_widget_undo.c \
tests/elementary/elm_test_focus_common.c \
tests/elementary/elm_test_focus.c \
tests/elementary/elm_test_focus_sub.c
tests/elementary/tests_elementary_elm_suite-elm_test_focus.$(OBJEXT): tests/elementary/focus_test.eo.c tests/elementary/focus_test.eo.h
tests/elementary/tests_elementary_elm_suite-elm_test_focus_sub.$(OBJEXT): tests/elementary/focus_test_sub.eo.c tests/elementary/focus_test_sub.eo.h
tests_elementary_elm_suite_CPPFLAGS = \
-DTESTS_BUILD_DIR=\"${top_builddir}/src/tests/elementary\" \

View File

@ -136,11 +136,25 @@ typedef struct _Elm_Version
EAPI extern Elm_Version *elm_version;
/* include these first for general used definitions */
#include <elm_gen.h>
#include <elm_general.h>
#include <elm_config.h>
#include <elm_focus.h>
#ifdef EFL_EO_API_SUPPORT
# include "efl_ui_focus_object.eo.h"
# include "efl_ui_focus_manager.eo.h"
# include "efl_ui_focus_manager_sub.eo.h"
# include "efl_ui_focus_user.eo.h"
#else
# include "efl_ui_focus_object.eo.legacy.h"
# include "efl_ui_focus_manager.eo.legacy.h"
# include "efl_ui_focus_manager_sub.eo.legacy.h"
# include "efl_ui_focus_user.eo.legacy.h"
#endif
#include <elm_tooltip.h>
#include <elm_object_item.h>
#include <elm_focus_item.h>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,129 @@
enum Efl.Ui.Focus.Direction {
right = 0,
left = 1,
down = 2,
up = 3,
next = 4,
prev = 5,
last = 6
}
class Efl.Ui.Focus.Manager (Efl.Object) {
methods {
move {
[[Move the focus into the given direction
This call flushes all changes.
This means all changes between the last flush and now are computed
]]
params {
direction : Efl.Ui.Focus.Direction; [[The direction to move to]]
}
return : Efl.Ui.Focus.Object; [[The element which is now focused]]
}
request_move {
[[Returns the object which would be the next object to focus in the given direction]]
params {
direction : Efl.Ui.Focus.Direction;
}
return : Efl.Ui.Focus.Object;
}
register {
[[Register a new item in the graph.
The parent has to be none null, it will be used as the parent in the logical tree.
The redirect argument will be set as redirect property on that manager, once child gets focused.
]]
params {
child : Efl.Ui.Focus.Object @nonull; [[The object to register]]
parent : Efl.Ui.Focus.Object @nonull; [[The parent to use in the logical tree]]
redirect : Efl.Ui.Focus.Manager; [[The redirect manager to set once this child is focused can be NULL for no redirect]]
}
return : bool; [[$true if it was successfull $false if not]]
}
update_redirect {
[[Set a new redirect object for the given child
Once the child is focused the redirect manager will be set in the redirect property.
Set to $null if nothing should happen
]]
params {
child : Efl.Ui.Focus.Object @nonull;
redirect : Efl.Ui.Focus.Manager; [[Once $child got focused this element will be set as redirect]]
}
return : bool;
}
update_parent {
[[Set a new logical parent for the given child]]
params {
child : Efl.Ui.Focus.Object @nonull; [[The child to update]]
parent : Efl.Ui.Focus.Object @nonull; [[The parent which now will be the logical parent of child]]
}
return : bool;
}
update_children {
[[Give the list of children a different order]]
params {
parent : Efl.Ui.Focus.Object @nonull; [[the parent to update]]
children : list<Efl.Ui.Focus.Object>; [[the list with the new order]]
}
return : bool;
}
unregister {
[[unregister the given item from the graph]]
params {
child : Efl.Ui.Focus.Object;
}
}
focus {
[[Make the given object the currently focused object in this manager.
The object has to be part of this manager object.
If you want to focus something in the redirect manager, just call the function on the redirect manager]]
params {
focus : Efl.Ui.Focus.Object @nonull;
}
}
@property redirect {
[[Add a another manager to serve the move requests.
If this value is set all move requests are redirected to this manager object.
Set it to $null once nothing should be redirected anymore.]]
values {
redirect : Efl.Ui.Focus.Manager;
}
}
@property border_elements {
[[The list of elements which are at the border of the graph
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 {
}
values {
border_elements : iterator<Efl.Ui.Focus.Object>;
}
}
@property root {
[[Root node for all logical subtrees.
This property can only be set once.
]]
values {
root : Efl.Ui.Focus.Object @nonull; [[Will be registered into this manager object]]
}
}
}
implements {
Efl.Object.constructor;
Efl.Object.finalize;
Efl.Object.provider_find;
Efl.Object.destructor;
}
events {
pre,flush; [[Emitted once the graph calculationg will be performed]]
coords,dirty; [[Emitted once the graph is dirty, this means there are potential changes in border_elements you want to know about]]
}
}

View File

@ -0,0 +1,188 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Elementary.h>
#include "elm_priv.h"
#define MY_CLASS EFL_UI_FOCUS_MANAGER_SUB_CLASS
#define MY_DATA(o, p) Efl_Ui_Focus_Manager_Sub_Data *pd = efl_data_scope_get(o, MY_CLASS);
typedef struct {
Efl_Ui_Focus_Manager *manager;
Efl_Ui_Focus_Manager *parent;
Eina_Bool self_dirty;
Eina_List *current_border;
} Efl_Ui_Focus_Manager_Sub_Data;
static Eina_List*
_set_a_without_b(Eina_List *a, Eina_List *b)
{
Eina_List *a_out = NULL, *node;
void *data;
a_out = eina_list_clone(a);
EINA_LIST_FOREACH(b, node, data)
{
a_out = eina_list_remove(a_out, data);
}
return a_out;
}
static void
_border_flush(Eo *obj, Efl_Ui_Focus_Manager_Sub_Data *pd)
{
Eina_Iterator *borders;
Eina_List *selection, *tmp;
Efl_Ui_Focus_Object *node;
borders = efl_ui_focus_manager_border_elements_get(obj);
selection = efl_ui_focus_manager_sub_select_set(obj, borders);
//elements which are not in the current border elements
tmp = eina_list_clone(pd->current_border);
tmp = _set_a_without_b(tmp , selection);
EINA_LIST_FREE(tmp, node)
{
efl_ui_focus_manager_unregister(pd->manager, node);
}
//set of the elements which are new without those which are currently registered
tmp = eina_list_clone(selection);
tmp = _set_a_without_b(tmp, pd->current_border);
EINA_LIST_FREE(tmp, node)
{
efl_ui_focus_manager_register(pd->manager, node, obj, obj);
}
eina_list_free(pd->current_border);
pd->current_border = selection;
}
static void
_border_unregister(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Sub_Data *pd)
{
Efl_Ui_Focus_Object *node;
EINA_LIST_FREE(pd->current_border, node)
{
efl_ui_focus_manager_unregister(pd->manager, node);
}
pd->current_border = NULL;
}
static void
_parent_manager_pre_flush(void *data, const Efl_Event *ev EINA_UNUSED)
{
MY_DATA(data, pd);
if (!pd->self_dirty) return; //we are not interested
_border_flush(data, pd);
}
EFL_CALLBACKS_ARRAY_DEFINE(parent_manager,
{EFL_UI_FOCUS_MANAGER_EVENT_PRE_FLUSH, _parent_manager_pre_flush}
);
static void
_parent_set(Eo *obj, Efl_Ui_Focus_Manager_Sub_Data *pd, Efl_Ui_Focus_Manager *manager)
{
if (pd->manager)
{
//remove ourself from the manager
efl_ui_focus_manager_unregister(pd->manager, obj);
efl_event_callback_array_del(pd->manager, parent_manager(), obj);
_border_unregister(obj, pd);
}
pd->manager = manager;
if (pd->manager)
{
//register our own root in the upper manager
efl_ui_focus_manager_register(pd->manager, obj, pd->parent, obj);
//listen to the manager
efl_event_callback_array_add(pd->manager, parent_manager(), obj);
_border_flush(obj, pd);
}
}
static void
_self_parent_change(void *data EINA_UNUSED, const Efl_Event *ev)
{
MY_DATA(ev->object , pd);
if (pd->manager == ev->info) return;
_parent_set(ev->object, pd, ev->info);
}
static void
_self_manager_dirty(void *data EINA_UNUSED, const Efl_Event *ev)
{
MY_DATA(ev->object , pd);
pd->self_dirty = EINA_TRUE;
}
EFL_CALLBACKS_ARRAY_DEFINE(self_manager,
{EFL_UI_FOCUS_USER_EVENT_MANAGER_CHANGED, _self_parent_change},
{EFL_UI_FOCUS_MANAGER_EVENT_COORDS_DIRTY, _self_manager_dirty},
);
EOLIAN static void
_efl_ui_focus_manager_sub_parent_set(Eo *obj, Efl_Ui_Focus_Manager_Sub_Data *pd, Efl_Ui_Focus_Object *node)
{
if (node == pd->parent) return;
pd->parent = node;
efl_ui_focus_manager_update_parent(pd->manager, obj, node);
}
EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_focus_manager_sub_parent_get(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Sub_Data *pd EINA_UNUSED)
{
return pd->parent;
}
EOLIAN static Efl_Object*
_efl_ui_focus_manager_sub_efl_object_constructor(Eo *obj, Efl_Ui_Focus_Manager_Sub_Data *pd EINA_UNUSED)
{
efl_event_callback_array_add(obj, self_manager(), NULL);
return efl_constructor(efl_super(obj, MY_CLASS));
}
EOLIAN static void
_efl_ui_focus_manager_sub_efl_object_destructor(Eo *obj, Efl_Ui_Focus_Manager_Sub_Data *pd)
{
efl_event_callback_array_del(obj, self_manager(), NULL);
_parent_set(obj, pd, NULL);
return efl_destructor(efl_super(obj, MY_CLASS));
}
EOLIAN static Efl_Object*
_efl_ui_focus_manager_sub_efl_object_finalize(Eo *obj, Efl_Ui_Focus_Manager_Sub_Data *pd)
{
Efl_Ui_Focus_Manager *manager;
manager = efl_ui_focus_user_manager_get(obj);
_parent_set(obj, pd, manager);
return efl_finalize(efl_super(obj, MY_CLASS));
}
#include "efl_ui_focus_manager_sub.eo.c"

View File

@ -0,0 +1,25 @@
abstract Efl.Ui.Focus.Manager.Sub (Efl.Ui.Focus.Manager, Efl.Ui.Focus.Object, Efl.Ui.Focus.User)
{
methods {
select_set {
params {
objects : iterator<Efl.Ui.Focus.Object>;
}
return : list<Efl.Ui.Focus.Object>;
}
@property parent {
values {
node : Efl.Ui.Focus.Object;
}
}
}
implements {
@empty .select_set;
@empty Efl.Ui.Focus.Object.geometry_get;
@empty Efl.Ui.Focus.Object.focus.get;
@empty Efl.Ui.Focus.User.manager.get;
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Object.finalize;
}
}

View File

@ -0,0 +1,30 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Elementary.h>
#include "elm_priv.h"
typedef struct {
} Efl_Ui_Focus_Object_Data;
EOLIAN static void
_efl_ui_focus_object_focus_set(Eo *obj, Efl_Ui_Focus_Object_Data *pd EINA_UNUSED, Eina_Bool focus)
{
const Efl_Event_Description *desc;
if (focus)
desc = EFL_UI_FOCUS_OBJECT_EVENT_FOCUS;
else
desc = EFL_UI_FOCUS_OBJECT_EVENT_UNFOCUS;
efl_event_callback_call(obj, desc, NULL);
}
#include "efl_ui_focus_object.eo.c"
typedef struct {
} Efl_Ui_Focus_User_Data;
#include "efl_ui_focus_user.eo.c"

View File

@ -0,0 +1,28 @@
mixin Efl.Ui.Focus.Object
{
[[Functions of focusable objects]]
methods {
geometry_get {
params {
@out rect : Eina.Rectangle;
}
}
@property focus {
[[This gets called by the manager and should never be called by someone else
It can be used by a implementation of a focus object to adapt to changes which are needed
]]
values {
focus : bool;
}
}
}
implements {
@empty .geometry_get;
@empty .focus.get;
}
events {
focus;
unfocus;
}
}

View File

@ -0,0 +1,18 @@
mixin Efl.Ui.Focus.User {
methods {
@property manager {
get {
}
values {
manager : Efl.Ui.Focus.Manager;
}
}
}
implements {
@empty .manager.get;
}
events {
manager,changed : Efl.Ui.Focus.Manager; [[emitted if a new manager is the parent for this one]]
}
}

View File

@ -83,6 +83,8 @@ static const Efl_Test_Case etc[] = {
{ "elm_code_widget_text", elm_code_test_widget_text },
{ "elm_code_widget_selection", elm_code_test_widget_selection },
{ "elm_code_widget_undo", elm_code_test_widget_undo },
{ "elm_focus", elm_test_focus},
{ "elm_focus_sub", elm_test_focus_sub},
{ NULL, NULL }
};

View File

@ -69,6 +69,8 @@ void elm_test_panes(TCase *tc);
void elm_test_slideshow(TCase *tc);
void elm_test_spinner(TCase *tc);
void elm_test_plug(TCase *tc);
void elm_test_focus(TCase *tc);
void elm_test_focus_sub(TCase *tc);
void elm_code_file_test_load(TCase *tc);
void elm_code_file_test_memory(TCase *tc);

View File

@ -0,0 +1,347 @@
#include "elm_test_focus_common.h"
START_TEST(focus_unregister_twice)
{
elm_init(1, NULL);
Efl_Ui_Focus_Object *r1 = efl_add(FOCUS_TEST_CLASS, NULL);
Efl_Ui_Focus_Object *r2 = efl_add(FOCUS_TEST_CLASS, NULL);
Efl_Ui_Focus_Manager *m = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, r1)
);
fail_if(!efl_ui_focus_manager_register(m, r2, r1, NULL));
efl_ui_focus_manager_unregister(m, r1);
efl_ui_focus_manager_unregister(m, r1);
efl_ui_focus_manager_unregister(m, r1);
efl_del(r2);
efl_del(r1);
efl_del(m);
elm_shutdown();
}
END_TEST
START_TEST(focus_register_twice)
{
elm_init(1, NULL);
Efl_Ui_Focus_Object *r1 = elm_focus_test_object_new("r1", 0, 0, 10, 10);
Efl_Ui_Focus_Object *r2 = elm_focus_test_object_new("r2", 0, 10, 10, 10);
Efl_Ui_Focus_Manager *m = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, r1)
);
fail_if(!efl_ui_focus_manager_register(m, r2, r1, NULL));
fail_if(efl_ui_focus_manager_register(m, r2, r1, NULL));
efl_del(r1);
efl_del(m);
elm_shutdown();
}
END_TEST
START_TEST(pos_check)
{
Efl_Ui_Focus_Manager *m;
Efl_Ui_Focus_Object *middle, *east, *west, *north, *south;
elm_init(1, NULL);
elm_focus_test_setup_cross(&middle, &south, &north, &east, &west);
m = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, middle)
);
efl_ui_focus_manager_register(m, north, middle, NULL);
efl_ui_focus_manager_register(m, south, middle, NULL);
efl_ui_focus_manager_register(m, west, middle, NULL);
efl_ui_focus_manager_register(m, east, middle, NULL);
#define CHECK(obj, r,l,u,d) \
efl_ui_focus_manager_focus(m, obj); \
ck_assert_ptr_eq(efl_ui_focus_manager_move(m, EFL_UI_FOCUS_DIRECTION_RIGHT), r); \
efl_ui_focus_manager_focus(m, obj); \
ck_assert_ptr_eq(efl_ui_focus_manager_move(m, EFL_UI_FOCUS_DIRECTION_LEFT), l); \
efl_ui_focus_manager_focus(m, obj); \
ck_assert_ptr_eq(efl_ui_focus_manager_move(m, EFL_UI_FOCUS_DIRECTION_UP), u); \
efl_ui_focus_manager_focus(m, obj); \
ck_assert_ptr_eq(efl_ui_focus_manager_move(m, EFL_UI_FOCUS_DIRECTION_DOWN), d); \
efl_ui_focus_manager_focus(m, obj);
CHECK(middle, east, west, north, south)
CHECK(east, NULL, middle, NULL, NULL)
CHECK(west, middle, NULL, NULL, NULL)
CHECK(north, NULL, NULL, NULL, middle)
CHECK(south, NULL, NULL, middle, NULL)
efl_del(middle);
efl_del(south);
efl_del(north);
efl_del(east);
efl_del(west);
elm_shutdown();
}
END_TEST
START_TEST(redirect)
{
elm_init(1, NULL);
TEST_OBJ_NEW(root, 0, 0, 20, 20);
TEST_OBJ_NEW(one, 0, 0, 20, 20);
TEST_OBJ_NEW(two, 20, 0, 20, 20);
Efl_Ui_Focus_Manager *m = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, root)
);
Efl_Ui_Focus_Manager *m2 = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, one)
);
efl_ui_focus_manager_register(m2, two, one, NULL);
efl_ui_focus_manager_redirect_set(m, m2);
efl_ui_focus_manager_focus(m2, one);
ck_assert_ptr_eq(efl_ui_focus_manager_move(m, EFL_UI_FOCUS_DIRECTION_RIGHT), two);
elm_shutdown();
}
END_TEST
START_TEST(border_check)
{
Efl_Ui_Focus_Manager *m;
Efl_Ui_Focus_Object *middle, *east, *west, *north, *south;
Eina_List *list = NULL;
Eina_Iterator *iter;
Efl_Ui_Focus_Object *obj;
elm_init(1, NULL);
elm_focus_test_setup_cross(&middle, &south, &north, &east, &west);
m = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, middle)
);
efl_ui_focus_manager_register(m, south, middle, NULL);
efl_ui_focus_manager_register(m, north, middle, NULL);
efl_ui_focus_manager_register(m, east, middle, NULL);
efl_ui_focus_manager_register(m, west, middle, NULL);
iter = efl_ui_focus_manager_border_elements_get(m);
EINA_ITERATOR_FOREACH(iter, obj)
{
list = eina_list_append(list, obj);
}
eina_iterator_free(iter);
ck_assert(eina_list_data_find(list, east) == east);
ck_assert(eina_list_data_find(list, north) == north);
ck_assert(eina_list_data_find(list, west) == west);
ck_assert(eina_list_data_find(list, east) == east);
ck_assert(eina_list_data_find(list, middle) == NULL);
ck_assert(eina_list_count(list) == 4);
elm_shutdown();
}
END_TEST
START_TEST(logical_chain)
{
Efl_Ui_Focus_Manager *m;
int i = 0;
elm_init(1, NULL);
TEST_OBJ_NEW(root, 0, 0, 20, 20);
m = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, root)
);
fail_if(!m);
efl_ui_focus_manager_focus(m, root);
i++;
TEST_OBJ_NEW(child1, 0, i*20, 20, 20);
i++;
TEST_OBJ_NEW(child2, 0, i*20, 20, 20);
i++;
TEST_OBJ_NEW(child3, 0, i*20, 20, 20);
i++;
TEST_OBJ_NEW(subchild11, 0, i*20, 20, 20);
i++;
TEST_OBJ_NEW(subchild12, 0, i*20, 20, 20);
i++;
TEST_OBJ_NEW(subchild13, 0, i*20, 20, 20);
i++;
TEST_OBJ_NEW(subchild21, 0, i*20, 20, 20);
i++;
TEST_OBJ_NEW(subchild22, 0, i*20, 20, 20);
i++;
TEST_OBJ_NEW(subchild23, 0, i*20, 20, 20);
//register everything
efl_ui_focus_manager_register(m, child1, root, NULL);
efl_ui_focus_manager_register(m, child2, root, NULL);
efl_ui_focus_manager_register(m, child3, root, NULL);
efl_ui_focus_manager_register(m, subchild11, child1, NULL);
efl_ui_focus_manager_register(m, subchild12, child1, NULL);
efl_ui_focus_manager_register(m, subchild13, child1, NULL);
efl_ui_focus_manager_register(m, subchild21, child3, NULL);
efl_ui_focus_manager_register(m, subchild22, child3, NULL);
efl_ui_focus_manager_register(m, subchild23, child3, NULL);
Efl_Object *logical_chain[] = {
child1, subchild11, subchild12, subchild13,
child2, child3, subchild21, subchild22, subchild23, root, NULL
};
for (i = 0; logical_chain[i]; ++i)
{
ck_assert_ptr_eq(logical_chain[i], efl_ui_focus_manager_move(m, EFL_UI_FOCUS_DIRECTION_NEXT));
}
i-= 2;
for (; i > 0; --i)
{
ck_assert_ptr_eq(logical_chain[i], efl_ui_focus_manager_move(m, EFL_UI_FOCUS_DIRECTION_PREV));
}
elm_shutdown();
}
END_TEST
START_TEST(finalize_check)
{
Efl_Ui_Focus_Manager *m;
elm_init(1, NULL);
m = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL);
fail_if(m);
elm_shutdown();
}
END_TEST
START_TEST(redirect_param)
{
Efl_Ui_Focus_Manager *m, *m2;
elm_init(1, NULL);
TEST_OBJ_NEW(root, 0, 20, 20, 20);
TEST_OBJ_NEW(root2, 0, 20, 20, 20);
TEST_OBJ_NEW(child, 0, 20, 20, 20);
m = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, root)
);
m2 = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, root2)
);
efl_ui_focus_manager_register(m, child, root, m2);
efl_ui_focus_manager_focus(m, child);
ck_assert_ptr_eq(efl_ui_focus_manager_redirect_get(m), m2);
elm_shutdown();
}
END_TEST
START_TEST(invalid_args_check)
{
Efl_Ui_Focus_Manager *m;
elm_init(1, NULL);
TEST_OBJ_NEW(root, 0, 20, 20, 20);
TEST_OBJ_NEW(child, 0, 20, 20, 20);
TEST_OBJ_NEW(child2, 0, 20, 20, 20);
m = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, root)
);
//no child and no parent
ck_assert_int_eq(efl_ui_focus_manager_register(m, NULL, NULL, NULL), 0);
ck_assert_int_eq(efl_ui_focus_manager_register(m, child, NULL, NULL), 0);
ck_assert_int_eq(efl_ui_focus_manager_register(m, NULL, root, NULL), 0);
ck_assert_int_eq(efl_ui_focus_manager_register(m, child, root, NULL), 1);
ck_assert_int_eq(efl_ui_focus_manager_update_parent(m, child, NULL), 0);
ck_assert_int_eq(efl_ui_focus_manager_update_parent(m, NULL, NULL), 0);
ck_assert_int_eq(efl_ui_focus_manager_update_parent(m, child, child2), 0);
ck_assert_int_eq(efl_ui_focus_manager_register(m, child2, root, NULL), 1);
ck_assert_int_eq(efl_ui_focus_manager_update_parent(m, child, child2), 1);
elm_shutdown();
}
END_TEST
START_TEST(order_check)
{
Efl_Ui_Focus_Manager *m;
Eina_List *order = NULL;
elm_init(1, NULL);
TEST_OBJ_NEW(root, 0, 20, 20, 20);
TEST_OBJ_NEW(child1, 0, 20, 20, 20);
TEST_OBJ_NEW(child2, 0, 20, 20, 20);
TEST_OBJ_NEW(child3, 0, 20, 20, 20);
m = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, root)
);
//no child and no parent
efl_ui_focus_manager_register(m, child1, root, NULL);
efl_ui_focus_manager_register(m, child2, root, NULL);
efl_ui_focus_manager_register(m, child3, root, NULL);
//positiv check
order = eina_list_append(order, child2);
order = eina_list_append(order, child3);
order = eina_list_append(order, child1);
ck_assert_int_eq(efl_ui_focus_manager_update_children(m, root, order), 1);
eina_list_free(order);
order = NULL;
//negativ check
order = eina_list_append(order, child1);
order = eina_list_append(order, child2);
ck_assert_int_eq(efl_ui_focus_manager_update_children(m, root, order), 0);
elm_shutdown();
}
END_TEST
void elm_test_focus(TCase *tc)
{
tcase_add_test(tc, focus_register_twice);
tcase_add_test(tc, focus_unregister_twice);
tcase_add_test(tc, pos_check);
tcase_add_test(tc, redirect);
tcase_add_test(tc, border_check);
tcase_add_test(tc, finalize_check);
tcase_add_test(tc, logical_chain);
tcase_add_test(tc, redirect_param);
tcase_add_test(tc, invalid_args_check);
tcase_add_test(tc, order_check);
}

View File

@ -0,0 +1,79 @@
#include "elm_test_focus_common.h"
#define Q(o,_x,_y,_w,_h) \
do {\
Eina_Rectangle rect = EINA_RECTANGLE_INIT; \
rect.x = _x; \
rect.y = _y; \
rect.w = _w; \
rect.h = _h; \
focus_test_size(o, rect); \
} while (0)
Efl_Ui_Focus_Object*
elm_focus_test_object_new(const char *name, int x, int y, int w, int h)
{
Efl_Ui_Focus_Object *ret;
ret = efl_add(FOCUS_TEST_CLASS, NULL,
efl_name_set(efl_added, name)
);
Q(ret, x, y, w, h);
return ret;
}
void
elm_focus_test_setup_cross(Efl_Ui_Focus_Object **middle,
Efl_Ui_Focus_Object **south,
Efl_Ui_Focus_Object **north,
Efl_Ui_Focus_Object **east,
Efl_Ui_Focus_Object **west)
{
*middle = elm_focus_test_object_new("middle", 40, 40, 20, 20);
*south = elm_focus_test_object_new("south", 40, 80, 20, 20);
*north = elm_focus_test_object_new("north", 40, 0, 20, 20);
*east = elm_focus_test_object_new("east", 80, 40, 20, 20);
*west = elm_focus_test_object_new("west", 0, 40, 20, 20);
}
//Test class implementation
typedef struct {
Eina_Rectangle rect;
Eina_Bool focus;
} Focus_Test_Data;
EOLIAN static Efl_Object*
_focus_test_efl_object_constructor(Eo *obj, Focus_Test_Data *pd)
{
Eo *eo;
eo = efl_constructor(efl_super(obj, FOCUS_TEST_CLASS));
eina_rectangle_coords_from(&pd->rect, 0, 0, 0, 0);
return eo;
}
EOLIAN static void
_focus_test_efl_ui_focus_object_focus_set(Eo *obj, Focus_Test_Data *pd, Eina_Bool focus)
{
pd->focus = focus;
printf("Object %p now focused\n", obj);
}
EOLIAN static void
_focus_test_efl_ui_focus_object_geometry_get(Eo *obj EINA_UNUSED, Focus_Test_Data *pd, Eina_Rectangle *rect)
{
if (!rect) return;
*rect = pd->rect;
}
EOLIAN static void
_focus_test_size(Eo *obj EINA_UNUSED, Focus_Test_Data *pd, Eina_Rectangle rect)
{
pd->rect = rect;
}
#include "focus_test.eo.c"

View File

@ -0,0 +1,28 @@
#ifndef ELM_TEST_FOCUS_COMMON_H
#define ELM_TEST_FOCUS_COMMON_H
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#define ELM_INTERFACE_ATSPI_ACCESSIBLE_PROTECTED
#define ELM_INTERNAL_API_ARGESFSDFEFC
#include <Elementary.h>
#include "elm_suite.h"
#include "elm_widget.h"
#include "focus_test.eo.h"
#define TEST_OBJ_NEW(name, x, y, w, h) \
Efl_Ui_Focus_Object* name; \
name = elm_focus_test_object_new("" #name "",x, y, w, h); \
Efl_Ui_Focus_Object* elm_focus_test_object_new(const char *name, int x, int y, int w, int h);
void elm_focus_test_setup_cross(Efl_Ui_Focus_Object **middle,
Efl_Ui_Focus_Object **south,
Efl_Ui_Focus_Object **north,
Efl_Ui_Focus_Object **east,
Efl_Ui_Focus_Object **west);
#endif

View File

@ -0,0 +1,242 @@
#include "elm_test_focus_common.h"
#include "focus_test_sub.eo.h"
typedef struct {
} Focus_Test_Sub_Data;
EOLIAN static Eina_List*
_focus_test_sub_efl_ui_focus_manager_sub_select_set(Eo *obj EINA_UNUSED, Focus_Test_Sub_Data *pd EINA_UNUSED, Eina_Iterator *objects)
{
Eina_List *list = NULL;
Efl_Ui_Focus_Object *o;
EINA_ITERATOR_FOREACH(objects, o)
{
list = eina_list_append(list, o);
}
eina_iterator_free(objects);
return list;
}
EOLIAN static void
_focus_test_sub_efl_ui_focus_object_geometry_get(Eo *obj EINA_UNUSED, Focus_Test_Sub_Data *pd EINA_UNUSED, Eina_Rectangle *rect EINA_UNUSED)
{
rect->y = rect->x = 0;
rect->w = rect->h = 20;
}
EOLIAN static Eina_Bool
_focus_test_sub_efl_ui_focus_object_focus_get(Eo *obj EINA_UNUSED, Focus_Test_Sub_Data *pd EINA_UNUSED)
{
return EINA_FALSE;
}
EOLIAN static Efl_Ui_Focus_Manager*
_focus_test_sub_efl_ui_focus_user_manager_get(Eo *obj, Focus_Test_Sub_Data *pd EINA_UNUSED)
{
return efl_parent_get(obj);
}
static Eina_List *registered;
static Eina_List *unregistered;
static Eina_Bool
_register(Eo *eo, void* data EINA_UNUSED, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Object *parent, Efl_Ui_Focus_Manager *manager)
{
registered = eina_list_append(registered, child);
printf("REGISTERED %p %s\n", child, efl_name_get(child));
return efl_ui_focus_manager_register(efl_super(eo, EFL_OBJECT_OVERRIDE_CLASS) , child, parent, manager);
}
static void
_unregister(Eo *eo, void* data EINA_UNUSED, Efl_Ui_Focus_Object *child)
{
unregistered = eina_list_append(unregistered, child);
printf("UNREGISTERED %p %s\n", child, efl_name_get(child));
efl_ui_focus_manager_unregister(efl_super(eo, EFL_OBJECT_OVERRIDE_CLASS) , child);
}
static Eina_Bool
_set_equal(Eina_List *a, Eina_List *b)
{
Eina_List *n;
void *d;
if (eina_list_count(a) != eina_list_count(b)) return EINA_FALSE;
EINA_LIST_FOREACH(a, n, d)
{
if (!eina_list_data_find(b, d)) return EINA_FALSE;
}
return EINA_TRUE;
}
#include "focus_test_sub.eo.c"
static void
_setup(Efl_Ui_Focus_Manager **m, Efl_Ui_Focus_Manager_Sub **sub, Efl_Ui_Focus_Object **r)
{
TEST_OBJ_NEW(root, 10, 10, 10, 10);
TEST_OBJ_NEW(root_manager, 0, 20, 20, 20);
EFL_OPS_DEFINE(manager_tracker,
EFL_OBJECT_OP_FUNC(efl_ui_focus_manager_register, _register),
EFL_OBJECT_OP_FUNC(efl_ui_focus_manager_unregister, _unregister),
);
Efl_Ui_Focus_Manager *manager = efl_add(EFL_UI_FOCUS_MANAGER_CLASS, NULL,
efl_ui_focus_manager_root_set(efl_added, root_manager)
);
//flush now all changes
efl_event_callback_call(manager, EFL_UI_FOCUS_MANAGER_EVENT_PRE_FLUSH, NULL);
registered = NULL;
unregistered = NULL;
efl_object_override(manager, &manager_tracker);
Efl_Ui_Focus_Manager_Sub *subm = efl_add(FOCUS_TEST_SUB_CLASS, manager,
efl_ui_focus_manager_sub_parent_set(efl_added, root_manager),
efl_ui_focus_manager_root_set(efl_added, root)
);
efl_event_callback_call(manager, EFL_UI_FOCUS_MANAGER_EVENT_PRE_FLUSH, NULL);
*sub = subm;
*m = manager;
*r = root;
}
START_TEST(correct_register)
{
Eina_List *set1 = NULL;
Efl_Ui_Focus_Object *root;
Efl_Ui_Focus_Manager *manager, *sub;
elm_init(0, NULL);
_setup(&manager, &sub, &root);
TEST_OBJ_NEW(child1, 0, 0, 10, 10);
TEST_OBJ_NEW(child2, 10, 0, 10, 10);
TEST_OBJ_NEW(child3, 0, 10, 10, 10);
set1 = eina_list_append(set1, sub);
set1 = eina_list_append(set1, root);
set1 = eina_list_append(set1, child1);
set1 = eina_list_append(set1, child2);
set1 = eina_list_append(set1, child3);
//test register stuff
efl_ui_focus_manager_register(sub, child1, root, NULL);
efl_ui_focus_manager_register(sub, child2, root, NULL);
efl_ui_focus_manager_register(sub, child3, root, NULL);
//now force submanager to flush things
efl_event_callback_call(manager, EFL_UI_FOCUS_MANAGER_EVENT_PRE_FLUSH, NULL);
ck_assert_ptr_eq(unregistered, NULL);
fail_if(!_set_equal(registered, set1));
efl_del(sub);
efl_del(manager);
efl_del(root);
efl_del(child1);
efl_del(child2);
efl_del(child3);
elm_shutdown();
}
END_TEST
START_TEST(correct_unregister)
{
Eina_List *set = NULL;
Efl_Ui_Focus_Object *root;
Efl_Ui_Focus_Manager *manager, *sub;
elm_init(0, NULL);
_setup(&manager, &sub, &root);
TEST_OBJ_NEW(child1, 0, 0, 10, 10);
TEST_OBJ_NEW(child2, 10, 0, 10, 10);
TEST_OBJ_NEW(child3, 0, 10, 10, 10);
set = eina_list_append(set, child3);
//test register stuff
efl_ui_focus_manager_register(sub, child1, root, NULL);
efl_ui_focus_manager_register(sub, child2, root, NULL);
efl_ui_focus_manager_register(sub, child3, root, NULL);
efl_event_callback_call(manager, EFL_UI_FOCUS_MANAGER_EVENT_PRE_FLUSH, NULL);
eina_list_free(unregistered);
unregistered = NULL;
eina_list_free(registered);
registered = NULL;
//test unregister stuff
efl_ui_focus_manager_unregister(sub, child3);
efl_event_callback_call(manager, EFL_UI_FOCUS_MANAGER_EVENT_PRE_FLUSH, NULL);
ck_assert_ptr_eq(registered, NULL);
fail_if(!_set_equal(unregistered, set));
eina_list_free(unregistered);
unregistered = NULL;
efl_del(sub);
efl_del(manager);
efl_del(root);
efl_del(child1);
efl_del(child2);
efl_del(child3);
elm_shutdown();
}
END_TEST
START_TEST(correct_un_register)
{
Eina_List *set_add = NULL, *set_del = NULL;
Efl_Ui_Focus_Object *root;
Efl_Ui_Focus_Manager *manager, *sub;
elm_init(0, NULL);
_setup(&manager, &sub, &root);
TEST_OBJ_NEW(child1, 0, 0, 10, 10);
TEST_OBJ_NEW(child2, 10, 0, 10, 10);
TEST_OBJ_NEW(child3, 0, 10, 10, 10);
set_add = eina_list_append(set_add, child2);
set_del = eina_list_append(set_del, child3);
//test register stuff
efl_ui_focus_manager_register(sub, child1, root, NULL);
efl_ui_focus_manager_register(sub, child3, root, NULL);
efl_event_callback_call(manager, EFL_UI_FOCUS_MANAGER_EVENT_PRE_FLUSH, NULL);
eina_list_free(unregistered);
unregistered = NULL;
eina_list_free(registered);
registered = NULL;
//test unregister stuff
efl_ui_focus_manager_unregister(sub, child3);
efl_ui_focus_manager_register(sub, child2, root, NULL);
efl_event_callback_call(manager, EFL_UI_FOCUS_MANAGER_EVENT_PRE_FLUSH, NULL);
fail_if(!_set_equal(registered, set_add));
fail_if(!_set_equal(unregistered, set_del));
efl_del(sub);
efl_del(manager);
efl_del(root);
efl_del(child1);
efl_del(child2);
efl_del(child3);
elm_shutdown();
}
END_TEST
void elm_test_focus_sub(TCase *tc)
{
tcase_add_test(tc, correct_register);
tcase_add_test(tc, correct_unregister);
tcase_add_test(tc, correct_un_register);
}

View File

@ -0,0 +1,14 @@
class Focus.Test(Efl.Object, Efl.Ui.Focus.Object) {
methods {
size {
params {
rect : Eina.Rectangle;
}
}
}
implements {
Efl.Object.constructor;
Efl.Ui.Focus.Object.geometry_get;
Efl.Ui.Focus.Object.focus.set;
}
}

View File

@ -0,0 +1,8 @@
class Focus.Test.Sub(Efl.Ui.Focus.Manager.Sub) {
implements {
Efl.Ui.Focus.Manager.Sub.select_set;
Efl.Ui.Focus.Object.geometry_get;
Efl.Ui.Focus.Object.focus.get;
Efl.Ui.Focus.User.manager.get;
}
}