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)
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)
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\" \
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>
--- /dev/null
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#include <Elementary.h>
+#include "elm_priv.h"
+
+#define MY_CLASS EFL_UI_FOCUS_MANAGER_CLASS
+#define FOCUS_DATA(obj) Efl_Ui_Focus_Manager_Data *pd = efl_data_scope_get(obj, MY_CLASS);
+
+#define DIM_EFL_UI_FOCUS_DIRECTION(dim,neg) dim*2+neg
+#define NODE_DIRECTIONS_COUNT 4
+
+#define DIRECTION_CHECK(dir) (dir >= 0 && dir < EFL_UI_FOCUS_DIRECTION_LAST)
+
+//#define DEBUG
+#define DEBUG_TUPLE(obj) efl_name_get(obj), efl_class_name_get(obj)
+
+typedef struct {
+ Eina_Bool positive;
+ Efl_Ui_Focus_Object *anchor;
+} Anchor;
+
+typedef enum {
+ DIMENSION_X = 0,
+ DIMENSION_Y = 1,
+} Dimension;
+
+typedef struct _Border Border;
+typedef struct _Node Node;
+
+struct _Border {
+ Eina_List *partners;
+};
+
+typedef enum {
+ NODE_TYPE_NORMAL = 0,
+ NODE_TYPE_LISTENER = 1,
+} Node_Type;
+
+struct _Node{
+ Node_Type type; //type of the node
+
+ Efl_Ui_Focus_Object *focusable;
+ Efl_Ui_Focus_Manager *manager;
+
+ union {
+ struct {
+ Efl_Ui_Focus_Manager *manager;
+ } listener;
+ struct {
+
+ } normal;
+ } data;
+
+ struct _Tree_Node{
+ Node *parent; //the parent in the tree
+ Eina_List *children; //this saves the original set of elements
+ }tree;
+
+ struct _Graph_Node {
+ Border directions[NODE_DIRECTIONS_COUNT];
+ } graph;
+};
+
+#define T(n) (n->tree)
+#define G(n) (n->graph)
+
+typedef struct {
+ Eina_List *focus_stack;
+ Eina_Hash *node_hash;
+ Efl_Ui_Focus_Manager *redirect;
+ Eina_List *dirty;
+
+ Node *root;
+} Efl_Ui_Focus_Manager_Data;
+
+static Efl_Ui_Focus_Direction
+_complement(Efl_Ui_Focus_Direction dir)
+{
+ #define COMP(a,b) \
+ if (dir == a) return b; \
+ if (dir == b) return a;
+
+ COMP(EFL_UI_FOCUS_DIRECTION_RIGHT, EFL_UI_FOCUS_DIRECTION_LEFT)
+ COMP(EFL_UI_FOCUS_DIRECTION_UP, EFL_UI_FOCUS_DIRECTION_DOWN)
+ COMP(EFL_UI_FOCUS_DIRECTION_PREV, EFL_UI_FOCUS_DIRECTION_NEXT)
+
+ #undef COMP
+
+ return EFL_UI_FOCUS_DIRECTION_LAST;
+}
+
+/*
+ * Set this new list of partners to the border.
+ * All old partners will be deleted
+ */
+static void
+border_partners_set(Node *node, Efl_Ui_Focus_Direction direction, Eina_List *list)
+{
+ Node *partner;
+ Eina_List *lnode;
+ Border *border = &G(node).directions[direction];
+
+ EINA_LIST_FREE(border->partners, partner)
+ {
+ Border *comp_border = &G(partner).directions[_complement(direction)];
+
+ comp_border->partners = eina_list_remove(comp_border->partners, node);
+ }
+
+ border->partners = list;
+
+ EINA_LIST_FOREACH(border->partners, lnode, partner)
+ {
+ Border *comp_border = &G(partner).directions[_complement(direction)];
+
+ comp_border->partners = eina_list_append(comp_border->partners, node);
+ }
+}
+
+/**
+ * Create a new node
+ */
+static Node*
+node_new(Efl_Ui_Focus_Object *focusable, Efl_Ui_Focus_Manager *manager)
+{
+ Node *node;
+
+ node = calloc(1, sizeof(Node));
+
+ node->focusable = focusable;
+ node->manager = manager;
+
+ return node;
+}
+
+static Node*
+node_get(Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *focusable)
+{
+ Node *ret;
+
+ ret = eina_hash_find(pd->node_hash, &focusable);
+
+ if (ret) return ret;
+
+ ERR("Focusable %p not registered in manager", focusable);
+
+ return NULL;
+}
+
+/**
+ * Free a node item and unlink this item from all direction
+ */
+static void
+node_item_free(Node *item)
+{
+ Node *n;
+ Eina_List *l;
+ //free the graph items
+ for(int i = 0;i < NODE_DIRECTIONS_COUNT; i++)
+ {
+ border_partners_set(item, i, NULL);
+ }
+
+ if (!item->tree.parent && item->tree.children)
+ {
+ ERR("Freeing the root with children is going to break the logical tree!");
+ }
+
+ if (item->tree.parent && item->tree.children)
+ {
+ Node *parent;
+
+ parent = item->tree.parent;
+ //reparent everything into the next layer
+ EINA_LIST_FOREACH(item->tree.children, l, n)
+ {
+ n->tree.parent = item->tree.parent;
+ }
+ parent->tree.children = eina_list_merge(parent->tree.children , item->tree.children);
+ }
+
+ if (item->tree.parent)
+ {
+ Node *parent;
+
+ parent = item->tree.parent;
+ T(parent).children = eina_list_remove(T(parent).children, item);
+ }
+
+ free(item);
+}
+
+
+//CALCULATING STUFF
+
+static inline int
+_distance(Eina_Rectangle node, Eina_Rectangle op, Dimension dim)
+{
+ int min, max, point;
+ int v1, v2;
+
+ if (dim == DIMENSION_X)
+ {
+ min = op.x;
+ max = eina_rectangle_max_x(&op);
+ point = node.x + node.w/2;
+ }
+ else
+ {
+ min = op.y;
+ max = eina_rectangle_max_y(&op);
+ point = node.y + node.h/2;
+ }
+
+ v1 = min - point;
+ v2 = max - point;
+
+ if (abs(v1) < abs(v2))
+ return v1;
+ else
+ return v2;
+}
+
+static inline void
+_calculate_node(Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *node, Dimension dim, Eina_List **pos, Eina_List **neg)
+{
+ Eina_Rectangle rect = EINA_RECTANGLE_INIT;
+ Efl_Ui_Focus_Object *op;
+ Efl_Ui_Focus_Object **focus_key;
+ int dim_min, dim_max;
+ Eina_Iterator *nodes;
+ int cur_pos_min = 0, cur_neg_min = 0;
+
+ nodes = eina_hash_iterator_key_new(pd->node_hash);
+ efl_ui_focus_object_geometry_get(node, &rect);
+
+ *pos = NULL;
+ *neg = NULL;
+
+ if (dim == DIMENSION_X)
+ {
+ dim_min = rect.y;
+ dim_max = rect.y + rect.h;
+ }
+ else
+ {
+ dim_min = rect.x;
+ dim_max = rect.x + rect.w;
+ }
+
+ EINA_ITERATOR_FOREACH(nodes, focus_key)
+ {
+ Eina_Rectangle op_rect = EINA_RECTANGLE_INIT;
+ int min, max;
+
+ op = *focus_key;
+ if (op == node) continue;
+
+ efl_ui_focus_object_geometry_get(op, &op_rect);
+
+ if (dim == DIMENSION_X)
+ {
+ min = op_rect.y;
+ max = eina_rectangle_max_y(&op_rect);
+ }
+ else
+ {
+ min = op_rect.x;
+ max = eina_rectangle_max_x(&op_rect);
+ }
+
+
+ /* two only way the calculation does make sense is if the two number
+ * lines are not disconnected.
+ * If they are connected one point of the 4 lies between the min and max of the other line
+ */
+ if (!((min <= max && max <= dim_min && dim_min <= dim_max) ||
+ (dim_min <= dim_max && dim_max <= min && min <= max)) &&
+ !eina_rectangle_intersection(&op_rect, &rect))
+ {
+ //this thing hits horizontal
+ int tmp_dis;
+
+ tmp_dis = _distance(rect, op_rect, dim);
+
+ if (tmp_dis < 0)
+ {
+ if (tmp_dis == cur_neg_min)
+ {
+ //add it
+ *neg = eina_list_append(*neg, op);
+ }
+ else if (tmp_dis > cur_neg_min
+ || cur_neg_min == 0) //init case
+ {
+ //nuke the old and add
+#ifdef DEBUG
+ printf("CORRECTION FOR %s-%s\n found anchor %s-%s in distance %d\n (%d,%d,%d,%d)\n (%d,%d,%d,%d)\n\n", DEBUG_TUPLE(node), DEBUG_TUPLE(op),
+ tmp_dis,
+ op_rect.x, op_rect.y, op_rect.w, op_rect.h,
+ rect.x, rect.y, rect.w, rect.h);
+#endif
+ *neg = eina_list_free(*neg);
+ *neg = eina_list_append(NULL, op);
+ cur_neg_min = tmp_dis;
+ }
+ }
+ else
+ {
+ if (tmp_dis == cur_pos_min)
+ {
+ //add it
+ *pos = eina_list_append(*pos, op);
+ }
+ else if (tmp_dis < cur_pos_min
+ || cur_pos_min == 0) //init case
+ {
+ //nuke the old and add
+#ifdef DEBUG
+ printf("CORRECTION FOR %s-%s\n found anchor %s-%s in distance %d\n (%d,%d,%d,%d)\n (%d,%d,%d,%d)\n\n", DEBUG_TUPLE(node), DEBUG_TUPLE(op),
+ tmp_dis,
+ op_rect.x, op_rect.y, op_rect.w, op_rect.h,
+ rect.x, rect.y, rect.w, rect.h);
+#endif
+ *pos = eina_list_free(*pos);
+ *pos = eina_list_append(NULL, op);
+ cur_pos_min = tmp_dis;
+ }
+ }
+
+
+#if 0
+ printf("(%d,%d,%d,%d)%s vs(%d,%d,%d,%d)%s\n", rect.x, rect.y, rect.w, rect.h, elm_widget_part_text_get(node, NULL), op_rect.x, op_rect.y, op_rect.w, op_rect.h, elm_widget_part_text_get(op, NULL));
+ printf("(%d,%d,%d,%d)\n", min, max, dim_min, dim_max);
+ printf("Candidate %d\n", tmp_dis);
+ if (anchor->anchor == NULL || abs(tmp_dis) < abs(distance)) //init case
+ {
+ distance = tmp_dis;
+ anchor->positive = tmp_dis > 0 ? EINA_FALSE : EINA_TRUE;
+ anchor->anchor = op;
+ //Helper for debugging wrong calculations
+
+ }
+#endif
+ }
+
+ }
+}
+
+#ifdef DEBUG
+static void
+_debug_node(Node *node)
+{
+ Eina_List *tmp = NULL;
+
+ if (!node) return;
+
+ printf("NODE %s-%s\n", DEBUG_TUPLE(node->focusable));
+
+#define DIR_LIST(dir) G(node).directions[dir].partners
+
+#define DIR_OUT(dir)\
+ tmp = DIR_LIST(dir); \
+ { \
+ Eina_List *list_node; \
+ Node *partner; \
+ printf("-"#dir"-> ("); \
+ EINA_LIST_FOREACH(tmp, list_node, partner) \
+ printf("%s-%s,", DEBUG_TUPLE(partner->focusable)); \
+ printf(")\n"); \
+ }
+
+ DIR_OUT(EFL_UI_FOCUS_DIRECTION_RIGHT)
+ DIR_OUT(EFL_UI_FOCUS_DIRECTION_LEFT)
+ DIR_OUT(EFL_UI_FOCUS_DIRECTION_UP)
+ DIR_OUT(EFL_UI_FOCUS_DIRECTION_DOWN)
+
+}
+#endif
+
+static void
+convert_border_set(Efl_Ui_Focus_Manager_Data *pd, Node *node, Eina_List *focusable_list, Efl_Ui_Focus_Direction dir)
+{
+ Eina_List *partners = NULL;
+ Efl_Ui_Focus_Object *obj;
+
+ EINA_LIST_FREE(focusable_list, obj)
+ {
+ Node *entry;
+
+ entry = node_get(pd, obj);
+ if (!entry)
+ {
+ CRI("Found a obj in graph without node-entry!");
+ return;
+ }
+ partners = eina_list_append(partners, entry);
+ }
+
+ border_partners_set(node, dir, partners);
+}
+
+static void
+dirty_flush(Efl_Ui_Focus_Manager *obj, Efl_Ui_Focus_Manager_Data *pd)
+{
+ Node *node;
+
+ efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_PRE_FLUSH, NULL);
+
+ EINA_LIST_FREE(pd->dirty, node)
+ {
+ Eina_List *x_partners_pos, *x_partners_neg;
+ Eina_List *y_partners_pos, *y_partners_neg;
+
+ _calculate_node(pd, node->focusable, DIMENSION_X, &x_partners_pos, &x_partners_neg);
+ _calculate_node(pd, node->focusable, DIMENSION_Y, &y_partners_pos, &y_partners_neg);
+
+ convert_border_set(pd, node, x_partners_pos, EFL_UI_FOCUS_DIRECTION_RIGHT);
+ convert_border_set(pd, node, x_partners_neg, EFL_UI_FOCUS_DIRECTION_LEFT);
+ convert_border_set(pd, node, y_partners_neg, EFL_UI_FOCUS_DIRECTION_UP);
+ convert_border_set(pd, node, y_partners_pos, EFL_UI_FOCUS_DIRECTION_DOWN);
+
+#ifdef DEBUG
+ _debug_node(node);
+#endif
+ }
+}
+static void
+dirty_add(Eo *obj, Efl_Ui_Focus_Manager_Data *pd, Node *dirty)
+{
+ //if (eina_list_data_find(pd->dirty, dirty)) return;
+ pd->dirty = eina_list_remove(pd->dirty, dirty);
+ pd->dirty = eina_list_append(pd->dirty, dirty);
+
+ efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_COORDS_DIRTY, NULL);
+}
+
+
+static void
+_node_new_geometery_cb(void *data, const Efl_Event *event)
+{
+ Node *node;
+ FOCUS_DATA(data)
+
+ node = node_get(pd, event->object);
+
+ dirty_add(data, pd, node);
+
+ return;
+}
+
+static void
+_focus_in_cb(void *data, const Efl_Event *event)
+{
+ efl_ui_focus_manager_focus(data, event->object);
+}
+
+static void
+_child_del(void *data, const Efl_Event *event)
+{
+ WRN("The manager itself catched a deletion of a child. BAD");
+ efl_ui_focus_manager_unregister(data, event->object);
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(focusable_node,
+ {EFL_EVENT_DEL, _child_del},
+ {EFL_GFX_EVENT_RESIZE, _node_new_geometery_cb},
+ {EFL_GFX_EVENT_MOVE, _node_new_geometery_cb},
+ //FIXME this is not correctly NOOOO ELM WIDGETS EVENTS HERE
+ {ELM_WIDGET_EVENT_FOCUSED, _focus_in_cb}
+);
+
+//=============================
+
+static Node*
+_register(Eo *obj, Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *child, Node *parent)
+{
+ Node *node;
+ if (!!eina_hash_find(pd->node_hash, &child))
+ {
+ ERR("Child %p is already registered in the graph", child);
+ return NULL;
+ }
+
+ node = node_new(child, obj);
+ eina_hash_add(pd->node_hash, &child, node);
+
+ //add the parent
+ if (parent)
+ {
+ T(node).parent = parent;
+ T(parent).children = eina_list_append(T(parent).children, node);
+ }
+
+ //listen to changes
+ efl_event_callback_array_add(child, focusable_node(), obj);
+
+ return node;
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_focus_manager_register(Eo *obj, Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Object *parent, Efl_Ui_Focus_Manager *redirect)
+{
+ Node *node = NULL;
+ Node *pnode = NULL;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(child, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(parent, EINA_FALSE);
+
+ pnode = node_get(pd, parent);
+ if (!pnode) return EINA_FALSE;
+
+ node = _register(obj, pd, child, pnode);
+ if (!node) return EINA_FALSE;
+
+ if (!redirect)
+ {
+ node->type = NODE_TYPE_NORMAL;
+ }
+ else
+ {
+ node->type = NODE_TYPE_LISTENER;
+ node->data.listener.manager = redirect;
+ }
+
+ //mark dirty
+ dirty_add(obj, pd, node);
+
+ return EINA_TRUE;
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_focus_manager_update_redirect(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Manager *redirect)
+{
+ Node *node = node_get(pd, child);
+
+ if (!node) return EINA_FALSE;
+ if (node->type != NODE_TYPE_LISTENER) return EINA_FALSE;
+
+ node->data.listener.manager = redirect;
+
+ return EINA_TRUE;
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_focus_manager_update_parent(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Object *parent_obj)
+{
+ Node *node;
+ Node *parent;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(parent_obj, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(child, EINA_FALSE);
+
+ node = node_get(pd, child);
+ parent = node_get(pd, parent_obj);
+
+ if (!node || !parent) return EINA_FALSE;
+
+ if (T(node).parent)
+ {
+ Node *old_parent;
+
+ old_parent = T(node).parent;
+
+ T(old_parent).children = eina_list_remove(T(old_parent).children, node);
+ }
+
+ T(node).parent = parent;
+
+ if (T(node).parent)
+ {
+ T(parent).children = eina_list_append(T(parent).children, node);
+ }
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_equal_set(Eina_List *none_nodes, Eina_List *nodes)
+{
+ Eina_List *n;
+ Node *node;
+
+ if (eina_list_count(nodes) != eina_list_count(none_nodes)) return EINA_FALSE;
+
+ EINA_LIST_FOREACH(nodes, n, node)
+ {
+ if (!eina_list_data_find(none_nodes, node->focusable))
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_focus_manager_update_children(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *parent, Eina_List *order)
+{
+ Node *pnode;
+
+ pnode = node_get(pd, parent);
+
+ if (!pnode)
+ return EINA_FALSE;
+
+ if (!_equal_set(order, T(pnode).children))
+ {
+ ERR("Set of children is not equal");
+ return EINA_FALSE;
+ }
+
+ T(pnode).children = order;
+
+ return EINA_TRUE;
+}
+
+EOLIAN static void
+_efl_ui_focus_manager_unregister(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *child)
+{
+ Node *node;
+
+ node = node_get(pd, child);
+
+ if (!node) return;
+
+ //remove the object from the stack if it hasnt dont that until now
+ //after this its not at the top anymore
+ //elm_widget_focus_set(node->focusable, EINA_FALSE);
+ //delete again from the list, for the case it was not at the top
+ pd->focus_stack = eina_list_remove(pd->focus_stack, node);
+
+ //add all neighboors of the node to the dirty list
+ for(int i = 0; i < 4; i++)
+ {
+ Node *partner;
+ Eina_List *n;
+
+ EINA_LIST_FOREACH(node->graph.directions[i].partners, n, partner)
+ {
+ dirty_add(obj, pd, partner);
+ }
+ }
+
+ //remove from the dirty parts
+ pd->dirty = eina_list_remove(pd->dirty, node);
+
+ eina_hash_del_by_key(pd->node_hash, &child);
+}
+
+EOLIAN static void
+_efl_ui_focus_manager_redirect_set(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Manager *redirect)
+{
+ if (pd->redirect == redirect) return;
+
+ if (pd->redirect)
+ efl_unref(pd->redirect);
+
+ pd->redirect = redirect;
+
+ if (pd->redirect)
+ efl_ref(pd->redirect);
+}
+
+EOLIAN static Efl_Ui_Focus_Manager *
+_efl_ui_focus_manager_redirect_get(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Data *pd)
+{
+ return pd->redirect;
+}
+
+static void
+_free_node(void *data)
+{
+ Node *node = data;
+ FOCUS_DATA(node->manager);
+
+ efl_event_callback_array_del(node->focusable, focusable_node(), node->manager);
+
+ if (pd->root != data)
+ {
+ node_item_free(node);
+ }
+}
+
+EOLIAN static Efl_Object *
+_efl_ui_focus_manager_efl_object_constructor(Eo *obj, Efl_Ui_Focus_Manager_Data *pd)
+{
+ pd->node_hash = eina_hash_pointer_new(_free_node);
+ return efl_constructor(efl_super(obj, MY_CLASS));
+}
+
+EOLIAN static Efl_Object *
+_efl_ui_focus_manager_efl_object_provider_find(Eo *obj, Efl_Ui_Focus_Manager_Data *pd EINA_UNUSED, const Efl_Object *klass)
+{
+ if (klass == MY_CLASS)
+ return obj;
+
+ return efl_provider_find(efl_super(obj, MY_CLASS), klass);
+}
+
+EOLIAN static void
+_efl_ui_focus_manager_efl_object_destructor(Eo *obj, Efl_Ui_Focus_Manager_Data *pd)
+{
+ eina_list_free(pd->focus_stack);
+ eina_list_free(pd->dirty);
+
+ eina_hash_free(pd->node_hash);
+
+ if (pd->root)
+ node_item_free(pd->root);
+ pd->root = NULL;
+
+ efl_destructor(efl_super(obj, MY_CLASS));
+}
+
+typedef struct {
+ Eina_Iterator iterator;
+ Eina_Iterator *real_iterator;
+ Efl_Ui_Focus_Manager *object;
+} Border_Elements_Iterator;
+
+static Eina_Bool
+_iterator_next(Border_Elements_Iterator *it, void **data)
+{
+ Node *node;
+
+ while(eina_iterator_next(it->real_iterator, (void**)&node))
+ {
+ for(int i = 0 ;i < NODE_DIRECTIONS_COUNT; i++)
+ {
+ if (!node->graph.directions[i].partners)
+ {
+ *data = node->focusable;
+ return EINA_TRUE;
+ }
+ }
+ }
+ return EINA_FALSE;
+}
+
+static Elm_Layout *
+_iterator_get_container(Border_Elements_Iterator *it)
+{
+ return it->object;
+}
+
+static void
+_iterator_free(Border_Elements_Iterator *it)
+{
+ eina_iterator_free(it->real_iterator);
+ free(it);
+}
+
+EOLIAN static Eina_Iterator*
+_efl_ui_focus_manager_border_elements_get(Eo *obj, Efl_Ui_Focus_Manager_Data *pd)
+{
+ Border_Elements_Iterator *it;
+
+ dirty_flush(obj, pd);
+
+ it = calloc(1, sizeof(Border_Elements_Iterator));
+
+ EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+ it->real_iterator = eina_hash_iterator_data_new(pd->node_hash);
+ it->iterator.version = EINA_ITERATOR_VERSION;
+ it->iterator.next = FUNC_ITERATOR_NEXT(_iterator_next);
+ it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_iterator_get_container);
+ it->iterator.free = FUNC_ITERATOR_FREE(_iterator_free);
+ it->object = obj;
+
+ return (Eina_Iterator*) it;
+}
+
+static Node*
+_no_history_element(Eina_Hash *node_hash)
+{
+ //nothing is selected yet, just try to use the first element in the iterator
+ Eina_Iterator *iter;
+ Node *upper;
+
+ iter = eina_hash_iterator_data_new(node_hash);
+
+ if (!eina_iterator_next(iter, (void**)&upper))
+ return NULL;
+ else
+ return upper;
+ eina_iterator_free(iter);
+}
+
+static Node*
+_coords_movement(Efl_Ui_Focus_Manager_Data *pd, Node *upper, Efl_Ui_Focus_Direction direction)
+{
+ Node *candidate;
+ Eina_List *node;
+
+ //we are searcing which of the partners is lower to the history
+ EINA_LIST_REVERSE_FOREACH(pd->focus_stack, node, candidate)
+ {
+ if (eina_list_data_find(G(upper).directions[direction].partners, candidate))
+ {
+ //this is the next accessable part
+ return candidate;
+ }
+ }
+
+ //if we havent found anything in the history, just use the first partner ... we have to start somewhere
+ //FIXME maybe decide coordinate wise?
+ return eina_list_data_get(G(upper).directions[direction].partners);
+}
+
+
+static Node*
+_parent_item(Node *node, Eina_Bool next)
+{
+ Node *parent;
+ Eina_List *lnode;
+
+ parent = T(node).parent;
+ lnode = eina_list_data_find_list(T(parent).children, node);
+
+ if (next)
+ lnode = eina_list_next(lnode);
+ else
+ lnode = eina_list_prev(lnode);
+
+ if (lnode)
+ return eina_list_data_get(lnode);
+ return NULL;
+}
+
+static Node*
+_next(Node *node)
+{
+ Node *n;
+
+ //Case 1 we are having children
+ if (T(node).children)
+ return eina_list_data_get(T(node).children);
+
+ //case 2 we are the root and we dont have children, return ourself
+ if (!T(node).parent)
+ return node;
+
+ //case 3 we are not at the end of the parents list
+ n = _parent_item(node, EINA_TRUE);
+ if (n)
+ return n;
+
+ //case 4 we are at the end of the parents list
+ n = node;
+ while(T(n).parent)
+ {
+ Node *parent_next;
+
+ parent_next = _parent_item(n, EINA_TRUE);
+
+ if (parent_next)
+ return parent_next;
+
+ n = T(n).parent;
+ }
+ //this is then the root again
+ return n;
+}
+
+static Node*
+_prev(Node *node)
+{
+ Node *n = NULL;
+
+ //this is the root there is no parent
+ if (!T(node).parent)
+ {
+ Node *subtree;
+
+ subtree = node;
+ //search the most down right item
+ while(T(subtree).children)
+ {
+ subtree = eina_list_last_data_get(T(subtree).children);
+ }
+ return subtree;
+ }
+
+ n =_parent_item(node, EINA_FALSE);
+ //case 1 there is a item in the parent previous to node, which has children
+ if (n && T(n).children)
+ return eina_list_last_data_get(T(n).children);
+
+ //case 2 there is a item in the parent preivous to node, which has no children
+ if (n)
+ return n;
+
+ //case 3 there is a no item in the parent provious to this one
+ //if (!n)
+ return T(node).parent;
+}
+
+
+static Node*
+_logical_movement(Efl_Ui_Focus_Manager_Data *pd EINA_UNUSED, Node *upper, Efl_Ui_Focus_Direction direction)
+{
+ if (direction == EFL_UI_FOCUS_DIRECTION_NEXT)
+ return _next(upper);
+ else
+ return _prev(upper);
+}
+
+EOLIAN static Efl_Ui_Focus_Object*
+_efl_ui_focus_manager_request_move(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Direction direction)
+{
+ dirty_flush(obj, pd);
+
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(DIRECTION_CHECK(direction), NULL);
+
+ if (pd->redirect)
+ return efl_ui_focus_manager_request_move(pd->redirect, direction);
+ else
+ {
+ Node *upper = NULL, *dir = NULL;
+
+ upper = eina_list_last_data_get(pd->focus_stack);
+
+ if (!upper)
+ {
+ upper = _no_history_element(pd->node_hash);
+ if (upper)
+ return upper->focusable;
+ return NULL;
+
+ }
+#ifdef DEBUG
+ _debug_node(upper);
+#endif
+ if (direction == EFL_UI_FOCUS_DIRECTION_PREV
+ || direction == EFL_UI_FOCUS_DIRECTION_NEXT)
+ dir = _logical_movement(pd, upper, direction);
+ else
+ dir = _coords_movement(pd, upper, direction);
+
+ //return the widget
+ if (dir)
+ return dir->focusable;
+ else
+ return NULL;
+ }
+}
+
+EOLIAN static void
+_efl_ui_focus_manager_focus(Eo *obj, Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *focus)
+{
+ Node *node;
+ Node *old_focus;
+
+ EINA_SAFETY_ON_NULL_RETURN(focus);
+
+ //check if node is part of this manager object
+ node = node_get(pd, focus);
+ if (!node) return;
+
+ //check if this is already the focused object
+ old_focus = eina_list_last_data_get(pd->focus_stack);
+
+ //check if this is already at the top
+ if (old_focus && old_focus->focusable == focus) return;
+
+ //remove the object from the list and add it again
+ pd->focus_stack = eina_list_remove(pd->focus_stack, node);
+ pd->focus_stack = eina_list_append(pd->focus_stack, node);
+
+ //populate the new change
+ if (old_focus) efl_ui_focus_object_focus_set(old_focus->focusable, EINA_FALSE);
+ efl_ui_focus_object_focus_set(node->focusable, EINA_TRUE);
+
+ //now check if this is also a listener object
+ if (node->type == NODE_TYPE_LISTENER)
+ {
+ Efl_Ui_Focus_Manager *redirect;
+
+ redirect = node->data.listener.manager;
+
+ efl_ui_focus_manager_redirect_set(obj, redirect);
+ }
+}
+
+EOLIAN static Efl_Ui_Focus_Object*
+_efl_ui_focus_manager_move(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Direction direction)
+{
+ Efl_Ui_Focus_Object *candidate;
+
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(DIRECTION_CHECK(direction), NULL);
+
+ if (pd->redirect)
+ {
+ return efl_ui_focus_manager_move(pd->redirect, direction);
+ }
+ else
+ {
+ candidate = efl_ui_focus_manager_request_move(obj, direction);
+ if (candidate)
+ efl_ui_focus_manager_focus(obj, candidate);
+ }
+
+#ifdef DEBUG
+ printf("Focus, MOVE %s %s\n", DEBUG_TUPLE(candidate));
+#endif
+ return candidate;
+}
+
+EOLIAN static void
+_efl_ui_focus_manager_root_set(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *root)
+{
+ Node *node;
+
+ if (pd->root)
+ {
+ ERR("Root element can only be set once!");
+ return;
+ }
+
+ node = _register(obj, pd, root, NULL);
+
+ pd->root = node;
+}
+
+EOLIAN static Efl_Ui_Focus_Object*
+_efl_ui_focus_manager_root_get(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Data *pd)
+{
+ if (!pd->root) return NULL;
+
+ return pd->root->focusable;
+}
+
+EOLIAN static Efl_Object*
+_efl_ui_focus_manager_efl_object_finalize(Eo *obj, Efl_Ui_Focus_Manager_Data *pd)
+{
+ Efl_Object *result;
+
+ if (!pd->root)
+ {
+ ERR("Constructing failed. No root element set.");
+ return NULL;
+ }
+
+ result = efl_finalize(efl_super(obj, MY_CLASS));
+
+ return result;
+}
+
+#include "efl_ui_focus_manager.eo.c"
\ No newline at end of file
--- /dev/null
+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]]
+ }
+}
\ No newline at end of file
--- /dev/null
+#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"
\ No newline at end of file
--- /dev/null
+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;
+ }
+}
\ No newline at end of file
--- /dev/null
+#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"
\ No newline at end of file
--- /dev/null
+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;
+ }
+}
\ No newline at end of file
--- /dev/null
+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]]
+ }
+}
\ No newline at end of file
{ "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 }
};
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);
--- /dev/null
+#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);
+}
--- /dev/null
+#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"
--- /dev/null
+#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
\ No newline at end of file
--- /dev/null
+#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);
+}
\ No newline at end of file
--- /dev/null
+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;
+ }
+}
\ No newline at end of file
--- /dev/null
+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;
+ }
+}
\ No newline at end of file