diff --git a/src/bin/elementary/test_ui_item_container.c b/src/bin/elementary/test_ui_item_container.c index e87a6ba9a7..e1e7654e5d 100644 --- a/src/bin/elementary/test_ui_item_container.c +++ b/src/bin/elementary/test_ui_item_container.c @@ -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"); diff --git a/src/lib/elementary/efl_ui_collection.c b/src/lib/elementary/efl_ui_collection.c index 2241c5209d..f1c5b568c3 100644 --- a/src/lib/elementary/efl_ui_collection.c +++ b/src/lib/elementary/efl_ui_collection.c @@ -11,6 +11,11 @@ #include #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" diff --git a/src/lib/elementary/efl_ui_collection_focus_manager.eo b/src/lib/elementary/efl_ui_collection_focus_manager.eo new file mode 100644 index 0000000000..6fbf57d89a --- /dev/null +++ b/src/lib/elementary/efl_ui_collection_focus_manager.eo @@ -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; + } +} diff --git a/src/lib/elementary/efl_ui_position_manager_common.h b/src/lib/elementary/efl_ui_position_manager_common.h index 56c7eeef08..7f84087b00 100644 --- a/src/lib/elementary/efl_ui_position_manager_common.h +++ b/src/lib/elementary/efl_ui_position_manager_common.h @@ -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); diff --git a/src/lib/elementary/efl_ui_position_manager_entity.eo b/src/lib/elementary/efl_ui_position_manager_entity.eo index 64c68344d8..fb3bb2d14a 100644 --- a/src/lib/elementary/efl_ui_position_manager_entity.eo +++ b/src/lib/elementary/efl_ui_position_manager_entity.eo @@ -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.]] diff --git a/src/lib/elementary/efl_ui_position_manager_grid.c b/src/lib/elementary/efl_ui_position_manager_grid.c index bf8f793d12..909f986f3e 100644 --- a/src/lib/elementary/efl_ui_position_manager_grid.c +++ b/src/lib/elementary/efl_ui_position_manager_grid.c @@ -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" diff --git a/src/lib/elementary/efl_ui_position_manager_grid.eo b/src/lib/elementary/efl_ui_position_manager_grid.eo index c7aba19da0..164b48ff52 100644 --- a/src/lib/elementary/efl_ui_position_manager_grid.eo +++ b/src/lib/elementary/efl_ui_position_manager_grid.eo @@ -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;} } } diff --git a/src/lib/elementary/efl_ui_position_manager_list.c b/src/lib/elementary/efl_ui_position_manager_list.c index e644a77736..d3cd973a06 100644 --- a/src/lib/elementary/efl_ui_position_manager_list.c +++ b/src/lib/elementary/efl_ui_position_manager_list.c @@ -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" diff --git a/src/lib/elementary/efl_ui_position_manager_list.eo b/src/lib/elementary/efl_ui_position_manager_list.eo index 5295612724..4764424131 100644 --- a/src/lib/elementary/efl_ui_position_manager_list.eo +++ b/src/lib/elementary/efl_ui_position_manager_list.eo @@ -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;} } } diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build index 8f0c85a12a..e42223682b 100644 --- a/src/lib/elementary/meson.build +++ b/src/lib/elementary/meson.build @@ -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 = []