efl_ui_collection: implement initial focus behaviour

Summary:
the behaviour here is that the next item according to the direction is
getting focused. This sounds easy but is quite complex given the fact
that the items might be hidden. This is the first draft for this, to see
how good it performes.

Reviewers: zmike, stefan_schmidt, cedric

Reviewed By: zmike

Subscribers: #reviewers, #committers

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D9496
This commit is contained in:
Marcel Hollerbach 2019-08-06 09:34:42 -04:00 committed by Mike Blumenkrantz
parent 49e8334024
commit 63d3af3ce9
10 changed files with 230 additions and 16 deletions

View File

@ -13,6 +13,16 @@ _selection_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
efl_ui_layout_orientation_set(data, EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL);
}
static void
_focus_item_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
Efl_Ui_Widget *element_0 = efl_pack_content_get(data, 0);
EINA_SAFETY_ON_NULL_RETURN(element_0);
efl_ui_focus_manager_focus_set(data, element_0);
}
static void
_scroll_to_animated_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
@ -168,29 +178,36 @@ void create_item_container_ui(Efl_Ui_Position_Manager_Entity *manager, const Efl
{
_add_item(o);
}
efl_pack_table(tbl, o, 1, 0, 1, 11);
efl_pack_table(tbl, o, 1, 0, 1, 12);
ctx->c = o;
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Focus item 0");
efl_event_callback_add(o, EFL_INPUT_EVENT_CLICKED, _focus_item_cb, item_container);
efl_pack_table(tbl, o, 0, 1, 1, 1);
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Scroll to 1154 ANIMATED");
efl_event_callback_add(o, EFL_INPUT_EVENT_CLICKED, _scroll_to_animated_cb, item_container);
efl_pack_table(tbl, o, 0, 1, 1, 1);
efl_pack_table(tbl, o, 0, 2, 1, 1);
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Scroll to 10");
efl_event_callback_add(o, EFL_INPUT_EVENT_CLICKED, _scroll_to_cb, item_container);
efl_pack_table(tbl, o, 0, 2, 1, 1);
efl_pack_table(tbl, o, 0, 3, 1, 1);
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Change min size of 0");
efl_event_callback_add(o, EFL_INPUT_EVENT_CLICKED, _change_min_size_cb, item_container);
efl_pack_table(tbl, o, 0, 3, 1, 1);
efl_pack_table(tbl, o, 0, 4, 1, 1);
o = efl_add(EFL_UI_CHECK_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
@ -198,14 +215,14 @@ void create_item_container_ui(Efl_Ui_Position_Manager_Entity *manager, const Efl
efl_text_set(o, "Vertical");
efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_cb, item_container);
efl_ui_check_selected_set(o, EINA_TRUE);
efl_pack_table(tbl, o, 0, 4, 1, 1);
efl_pack_table(tbl, o, 0, 5, 1, 1);
o = efl_add(EFL_UI_CHECK_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Match Vertical");
efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_match_content_cb, ctx);
efl_pack_table(tbl, o, 0, 5, 1, 1);
efl_pack_table(tbl, o, 0, 6, 1, 1);
ctx->v = o;
o = efl_add(EFL_UI_CHECK_CLASS, tbl,
@ -213,7 +230,7 @@ void create_item_container_ui(Efl_Ui_Position_Manager_Entity *manager, const Efl
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Match Horizontal");
efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_match_content_cb, ctx);
efl_pack_table(tbl, o, 0, 6, 1, 1);
efl_pack_table(tbl, o, 0, 7, 1, 1);
efl_gfx_entity_size_set(win, EINA_SIZE2D(260, 200));
ctx->h = o;
@ -222,27 +239,27 @@ void create_item_container_ui(Efl_Ui_Position_Manager_Entity *manager, const Efl
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Remove all items");
efl_event_callback_add(o, EFL_INPUT_EVENT_CLICKED, _remove_all_cb, item_container);
efl_pack_table(tbl, o, 0, 7, 1, 1);
efl_pack_table(tbl, o, 0, 8, 1, 1);
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Add 1 item");
efl_event_callback_add(o, EFL_INPUT_EVENT_CLICKED, _add_one_item, item_container);
efl_pack_table(tbl, o, 0, 8, 1, 1);
efl_pack_table(tbl, o, 0, 9, 1, 1);
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Add 1000 item");
efl_event_callback_add(o, EFL_INPUT_EVENT_CLICKED, _add_thousend_items, item_container);
efl_pack_table(tbl, o, 0, 9, 1, 1);
efl_pack_table(tbl, o, 0, 10, 1, 1);
bx = efl_add(EFL_UI_RADIO_BOX_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_event_callback_add(bx, EFL_UI_RADIO_GROUP_EVENT_VALUE_CHANGED, _select_value_cb, item_container);
efl_pack_table(tbl, bx, 0, 10, 1, 1);
efl_pack_table(tbl, bx, 0, 11, 1, 1);
o = efl_add(EFL_UI_RADIO_CLASS, bx,
efl_ui_radio_state_value_set(efl_added, EFL_UI_SELECT_MODE_SINGLE));
efl_text_set(o, "Singleselect");

View File

@ -11,6 +11,11 @@
#include <Elementary.h>
#include "elm_widget.h"
#include "elm_priv.h"
#include "efl_ui_collection_focus_manager.eo.h"
typedef struct {
Eo *collection;
} Efl_Ui_Collection_Focus_Manager_Data;
typedef struct {
unsigned int last_index;
@ -138,6 +143,7 @@ typedef struct {
Fast_Accessor obj_accessor;
Fast_Accessor size_accessor;
Efl_Gfx_Entity *sizer;
unsigned int start_id, end_id;
} Efl_Ui_Collection_Data;
static Eina_Bool register_item(Eo *obj, Efl_Ui_Collection_Data *pd, Efl_Ui_Item *item);
@ -745,9 +751,20 @@ _pos_content_min_size_changed_cb(void *data EINA_UNUSED, const Efl_Event *ev)
flush_min_size(data, pd);
}
static void
_visible_range_changed_cb(void *data EINA_UNUSED, const Efl_Event *ev)
{
Efl_Ui_Position_Manager_Range_Update *info = ev->info;
MY_DATA_GET(data, pd);
pd->start_id = info->start_id;
pd->end_id = info->end_id;
}
EFL_CALLBACKS_ARRAY_DEFINE(pos_manager_cbs,
{EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_SIZE_CHANGED, _pos_content_size_changed_cb},
{EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_MIN_SIZE_CHANGED, _pos_content_min_size_changed_cb},
{EFL_UI_POSITION_MANAGER_ENTITY_EVENT_VISIBLE_RANGE_CHANGED, _visible_range_changed_cb}
)
EOLIAN static void
@ -786,8 +803,11 @@ _efl_ui_collection_position_manager_get(const Eo *obj EINA_UNUSED, Efl_Ui_Collec
EOLIAN static Efl_Ui_Focus_Manager*
_efl_ui_collection_efl_ui_widget_focus_manager_focus_manager_create(Eo *obj, Efl_Ui_Collection_Data *pd EINA_UNUSED, Efl_Ui_Focus_Object *root)
{
return efl_add(EFL_UI_FOCUS_MANAGER_CALC_CLASS, obj,
efl_ui_focus_manager_root_set(efl_added, root));
Eo *man = efl_add(EFL_UI_COLLECTION_FOCUS_MANAGER_CLASS, obj,
efl_ui_focus_manager_root_set(efl_added, root));
Efl_Ui_Collection_Focus_Manager_Data *fm_pd = efl_data_scope_safe_get(man, EFL_UI_COLLECTION_FOCUS_MANAGER_CLASS);
fm_pd->collection = obj;
return man;
}
EOLIAN static Eina_Bool
@ -796,6 +816,19 @@ _efl_ui_collection_efl_ui_widget_focus_state_apply(Eo *obj, Efl_Ui_Collection_Da
return efl_ui_widget_focus_state_apply(efl_super(obj, MY_CLASS), current_state, configured_state, obj);
}
static Efl_Ui_Item *
_find_item(Eo *obj EINA_UNUSED, Efl_Ui_Collection_Data *pd EINA_UNUSED, Eo *focused_element)
{
if (!focused_element) return NULL;
while (focused_element && !efl_isa(focused_element, EFL_UI_ITEM_CLASS))
{
focused_element = efl_ui_widget_parent_get(focused_element);
}
return focused_element;
}
EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_collection_efl_ui_focus_manager_move(Eo *obj, Efl_Ui_Collection_Data *pd, Efl_Ui_Focus_Direction direction)
{
@ -803,7 +836,10 @@ _efl_ui_collection_efl_ui_focus_manager_move(Eo *obj, Efl_Ui_Collection_Data *pd
Eina_Size2D step;
focus = efl_ui_focus_manager_focus_get(obj);
if (focus)
new_obj = efl_ui_focus_manager_move(efl_super(obj, MY_CLASS), direction);
step = efl_gfx_hint_size_combined_min_get(focus);
if (new_obj)
{
/* if this is outside the viewport, then we must bring that in first */
Eina_Rect viewport;
@ -817,8 +853,7 @@ _efl_ui_collection_efl_ui_focus_manager_move(Eo *obj, Efl_Ui_Collection_Data *pd
return focus;
}
}
new_obj = efl_ui_focus_manager_move(efl_super(obj, MY_CLASS), direction);
step = efl_gfx_hint_size_min_get(focus);
if (!new_obj)
{
Eina_Rect pos = efl_gfx_entity_geometry_get(focus);
@ -875,3 +910,83 @@ _efl_ui_collection_efl_ui_focus_manager_move(Eo *obj, Efl_Ui_Collection_Data *pd
}
#include "efl_ui_collection.eo.c"
#define ITEM_IS_OUTSIDE_VISIBLE(id) id < collection_pd->start_id || id > collection_pd->end_id
static inline void
_assert_item_available(Eo *item, int new_id, Efl_Ui_Collection_Data *pd)
{
efl_gfx_entity_visible_set(item, EINA_TRUE);
efl_gfx_entity_geometry_set(item, efl_ui_position_manager_entity_position_single_item(pd->pos_man, new_id));
}
EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_collection_focus_manager_efl_ui_focus_manager_request_move(Eo *obj, Efl_Ui_Collection_Focus_Manager_Data *pd, Efl_Ui_Focus_Direction direction, Efl_Ui_Focus_Object *child, Eina_Bool logical)
{
MY_DATA_GET(pd->collection, collection_pd);
Efl_Ui_Item *new_item, *item;
unsigned int item_id;
if (!child)
child = efl_ui_focus_manager_focus_get(obj);
item = _find_item(obj, collection_pd, child);
//if this is NULL then we are before finalize, we cannot serve any sane value here
if (!collection_pd->pos_man) return NULL;
EINA_SAFETY_ON_NULL_RETURN_VAL(item, NULL);
item_id = efl_ui_item_index_get(item);
if (ITEM_IS_OUTSIDE_VISIBLE(item_id))
{
int new_id = efl_ui_position_manager_entity_relative_item(collection_pd->pos_man, efl_ui_item_index_get(item), direction);
if (new_id == -1)
{
new_item = NULL;
}
else
{
new_item = eina_list_nth(collection_pd->items, new_id);;
_assert_item_available(new_item, new_id, collection_pd);
}
}
else
{
new_item = efl_ui_focus_manager_request_move(efl_super(obj, EFL_UI_COLLECTION_FOCUS_MANAGER_CLASS), direction, child, logical);
}
return new_item;
}
EOLIAN static void
_efl_ui_collection_focus_manager_efl_ui_focus_manager_manager_focus_set(Eo *obj, Efl_Ui_Collection_Focus_Manager_Data *pd, Efl_Ui_Focus_Object *focus)
{
MY_DATA_GET(pd->collection, collection_pd);
Efl_Ui_Item *item;
unsigned int item_id;
if (focus == efl_ui_focus_manager_root_get(obj))
{
item = eina_list_data_get(collection_pd->items);
}
else
{
item = _find_item(obj, collection_pd, focus);
}
//if this is NULL then we are before finalize, we cannot serve any sane value here
if (!collection_pd->pos_man) return;
EINA_SAFETY_ON_NULL_RETURN(item);
item_id = efl_ui_item_index_get(item);
if (ITEM_IS_OUTSIDE_VISIBLE(item_id))
{
_assert_item_available(item, item_id, collection_pd);
}
efl_ui_focus_manager_focus_set(efl_super(obj, EFL_UI_COLLECTION_FOCUS_MANAGER_CLASS), focus);
}
#include "efl_ui_collection_focus_manager.eo.c"

View File

@ -0,0 +1,7 @@
class @beta Efl.Ui.Collection.Focus_Manager extends Efl.Ui.Focus.Manager_Calc {
[[Internal class which implements collection specific behaviour, cannot be used outside of collection]]
implements {
Efl.Ui.Focus.Manager.manager_focus { set; }
Efl.Ui.Focus.Manager.request_move;
}
}

View File

@ -39,6 +39,11 @@ vis_change_segment(Api_Callback *cb, int a, int b, Eina_Bool flag)
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(cb, MIN(a,b), len, data) >= 0);
}
ent = data[i - MIN(a,b)];
if (ent && !flag && (efl_ui_focus_object_focus_get(ent) || efl_ui_focus_object_child_focus_get(ent)))
{
//we should not make focused object invisible, rather move it to some parking lot
efl_gfx_entity_position_set(ent, EINA_POSITION2D(-9999,-9999));
}
if (ent && !efl_ui_focus_object_focus_get(ent))
{
efl_gfx_entity_visible_set(ent, flag);

View File

@ -1,3 +1,4 @@
import efl_ui;
function Efl.Ui.Position_Manager.Batch_Access_Entity {
[[ Function callback for getting a batch of items]]
@ -114,6 +115,14 @@ interface @beta Efl.Ui.Position_Manager.Entity extends Efl.Ui.Layout_Orientable
end_id : int; [[The last item that has a new size]]
}
}
relative_item {
[[translate the current_id, into a new id which is oriented in the $direction of $current_id. In case that there is no item, -1 is returned]]
params {
current_id : uint; [[The id where the direction is oriented at]]
direction : Efl.Ui.Focus.Direction; [[The direction where the new id is]]
}
return : int; [[The id of the item in that direction, or -1 if there is no item in that direction]]
}
}
events {
content_size,changed : Eina.Size2D; [[Emitted when the aggregate size of all items has changed. This can be used to resize an enclosing Pan object.]]

View File

@ -329,4 +329,35 @@ _efl_ui_position_manager_grid_efl_ui_position_manager_entity_position_single_ite
return geom;
}
EOLIAN static int
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_relative_item(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, unsigned int current_id, Efl_Ui_Focus_Direction direction)
{
int new_id = current_id;
switch(direction)
{
case EFL_UI_FOCUS_DIRECTION_RIGHT:
case EFL_UI_FOCUS_DIRECTION_NEXT:
new_id += 1;
break;
case EFL_UI_FOCUS_DIRECTION_LEFT:
case EFL_UI_FOCUS_DIRECTION_PREVIOUS:
new_id -= 1;
break;
case EFL_UI_FOCUS_DIRECTION_UP:
new_id -= pd->current_display_table.columns;
break;
case EFL_UI_FOCUS_DIRECTION_DOWN:
new_id += pd->current_display_table.columns;
break;
default:
new_id = -1;
ERR("Uncaught case!");
break;
}
if (new_id < 0 || new_id > (int)pd->size)
return -1;
else
return new_id;
}
#include "efl_ui_position_manager_grid.eo.c"

View File

@ -12,6 +12,7 @@ class @beta Efl.Ui.Position_Manager.Grid extends Efl.Object implements Efl.Ui.Po
Efl.Ui.Position_Manager.Entity.item_removed;
Efl.Ui.Position_Manager.Entity.position_single_item;
Efl.Ui.Position_Manager.Entity.item_size_changed;
Efl.Ui.Position_Manager.Entity.relative_item;
Efl.Ui.Layout_Orientable.orientation {set; get;}
}
}

View File

@ -407,5 +407,32 @@ _efl_ui_position_manager_list_efl_object_destructor(Eo *obj, Efl_Ui_Position_Man
efl_destructor(efl_super(obj, MY_CLASS));
}
EOLIAN static int
_efl_ui_position_manager_list_efl_ui_position_manager_entity_relative_item(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_List_Data *pd, unsigned int current_id, Efl_Ui_Focus_Direction direction)
{
int new_id = current_id;
switch(direction)
{
case EFL_UI_FOCUS_DIRECTION_RIGHT:
case EFL_UI_FOCUS_DIRECTION_NEXT:
case EFL_UI_FOCUS_DIRECTION_DOWN:
new_id += 1;
break;
case EFL_UI_FOCUS_DIRECTION_LEFT:
case EFL_UI_FOCUS_DIRECTION_PREVIOUS:
case EFL_UI_FOCUS_DIRECTION_UP:
new_id -= 1;
break;
default:
ERR("Uncaught case!");
new_id = -1;
break;
}
if (new_id < 0 || new_id > (int)pd->size)
return -1;
else
return new_id;
}
#include "efl_ui_position_manager_list.eo.c"

View File

@ -13,6 +13,7 @@ class @beta Efl.Ui.Position_Manager.List extends Efl.Object implements Efl.Ui.Po
Efl.Ui.Position_Manager.Entity.item_removed;
Efl.Ui.Position_Manager.Entity.position_single_item;
Efl.Ui.Position_Manager.Entity.item_size_changed;
Efl.Ui.Position_Manager.Entity.relative_item;
Efl.Ui.Layout_Orientable.orientation {set; get;}
}
}

View File

@ -237,6 +237,7 @@ priv_eo_files = [
'efl_ui_exact_model.eo',
'efl_ui_average_model.eo',
'efl_ui_spotlight_manager_plain.eo',
'efl_ui_collection_focus_manager.eo',
]
priv_eo_file_target = []