forked from enlightenment/efl
elementary: improve focus memory for Efl.Ui.CollectionView.
This patch will make the CollectionView remember at all time the last focus object and the last item in the list. Reviewed-by: Marcel Hollerbach <mail@marcel-hollerbach.de> Differential Revision: https://phab.enlightenment.org/D10677
This commit is contained in:
parent
e583b9159e
commit
6756485476
|
@ -79,6 +79,11 @@ struct _Efl_Ui_Collection_View_Data
|
||||||
|
|
||||||
Eina_List *requests; // Array of Efl_Ui_Collection_Request in progress
|
Eina_List *requests; // Array of Efl_Ui_Collection_Request in progress
|
||||||
|
|
||||||
|
struct {
|
||||||
|
Efl_Gfx_Entity *last; // The last item of the collection, so focus can start by the end if necessary.
|
||||||
|
Efl_Gfx_Entity *previously; // The previously selected item in the collection, so focus can come back to it.
|
||||||
|
} focus;
|
||||||
|
|
||||||
unsigned int start_id;
|
unsigned int start_id;
|
||||||
unsigned int end_id;
|
unsigned int end_id;
|
||||||
|
|
||||||
|
@ -293,6 +298,9 @@ _all_cleanup(Efl_Ui_Collection_View *obj, Efl_Ui_Collection_View_Data *pd)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
efl_replace(&pd->focus.previously, NULL);
|
||||||
|
efl_replace(&pd->focus.last, NULL);
|
||||||
|
|
||||||
EINA_LIST_FOREACH_SAFE(pd->requests, l, ll, request)
|
EINA_LIST_FOREACH_SAFE(pd->requests, l, ll, request)
|
||||||
eina_future_cancel(request->f);
|
eina_future_cancel(request->f);
|
||||||
}
|
}
|
||||||
|
@ -485,6 +493,33 @@ _entity_fetch_cb(Eo *obj, void *data EINA_UNUSED, const Eina_Value v)
|
||||||
return eina_future_as_value(r);
|
return eina_future_as_value(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline unsigned int
|
||||||
|
_lookup_entity_index(Efl_Gfx_Entity *entity, Efl_Model **model)
|
||||||
|
{
|
||||||
|
Efl_Model *fetch;
|
||||||
|
|
||||||
|
fetch = efl_ui_view_model_get(entity);
|
||||||
|
if (model) *model = fetch;
|
||||||
|
return efl_composite_model_index_get(fetch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_last_entity_update(Efl_Ui_Collection_View_Data *pd, Efl_Gfx_Entity *entity)
|
||||||
|
{
|
||||||
|
Efl_Model *new_model, *old_model;
|
||||||
|
unsigned int new_index, old_index;
|
||||||
|
|
||||||
|
if (!pd->focus.last) goto replace;
|
||||||
|
|
||||||
|
new_index = _lookup_entity_index(entity, &new_model);
|
||||||
|
old_index = _lookup_entity_index(pd->focus.last, &old_model);
|
||||||
|
|
||||||
|
if (new_index <= old_index) return;
|
||||||
|
|
||||||
|
replace:
|
||||||
|
efl_replace(&pd->focus.last, entity);
|
||||||
|
}
|
||||||
|
|
||||||
static inline Eina_Bool
|
static inline Eina_Bool
|
||||||
_entity_propagate(Efl_Model *model, Efl_Gfx_Entity *entity)
|
_entity_propagate(Efl_Model *model, Efl_Gfx_Entity *entity)
|
||||||
{
|
{
|
||||||
|
@ -634,6 +669,9 @@ _entity_fetched_cb(Eo *obj, void *data, const Eina_Value v)
|
||||||
evas_event_thaw(e);
|
evas_event_thaw(e);
|
||||||
evas_event_thaw_eval(e);
|
evas_event_thaw_eval(e);
|
||||||
|
|
||||||
|
// Check if the last child is also the list item in the list
|
||||||
|
_last_entity_update(pd, child);
|
||||||
|
|
||||||
// Currently position manager will flush its entire size cache on update, so only do
|
// Currently position manager will flush its entire size cache on update, so only do
|
||||||
// it when necessary to improve performance.
|
// it when necessary to improve performance.
|
||||||
if (updated_size || request->need_size)
|
if (updated_size || request->need_size)
|
||||||
|
@ -667,6 +705,54 @@ _entity_free_cb(Eo *o, void *data, const Eina_Future *dead_future EINA_UNUSED)
|
||||||
free(request);
|
free(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_focus_lookup(Efl_Ui_Collection_View_Data *pd, unsigned int search_index,
|
||||||
|
Efl_Gfx_Entity **entity, Efl_Model **model)
|
||||||
|
{
|
||||||
|
unsigned int idx;
|
||||||
|
|
||||||
|
if (entity) *entity = pd->focus.last;
|
||||||
|
if (pd->focus.last)
|
||||||
|
{
|
||||||
|
idx = _lookup_entity_index(pd->focus.last, model);
|
||||||
|
if (idx == search_index) return EINA_TRUE;
|
||||||
|
}
|
||||||
|
if (entity) *entity = pd->focus.previously;
|
||||||
|
if (pd->focus.previously)
|
||||||
|
{
|
||||||
|
idx = _lookup_entity_index(pd->focus.previously, model);
|
||||||
|
if (idx == search_index) return EINA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity) *entity = NULL;
|
||||||
|
if (model) *model = NULL;
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Efl_Ui_Collection_Item_Lookup *
|
||||||
|
_build_from_focus(Efl_Ui_Collection_View_Data *pd, unsigned int search_index,
|
||||||
|
Efl_Model **model)
|
||||||
|
{
|
||||||
|
Efl_Ui_Collection_Item_Lookup *insert;
|
||||||
|
Efl_Gfx_Entity *entity = NULL;
|
||||||
|
|
||||||
|
// Not found in the cache lookup, but just maybe
|
||||||
|
if (!_focus_lookup(pd, search_index, &entity, model)) return NULL;
|
||||||
|
|
||||||
|
// Lucky us, let's add it to the cache
|
||||||
|
insert = calloc(1, sizeof (Efl_Ui_Collection_Item_Lookup));
|
||||||
|
if (!insert) return NULL;
|
||||||
|
|
||||||
|
insert->index = search_index;
|
||||||
|
insert->item.model = efl_ref(*model);
|
||||||
|
insert->item.entity = efl_ref(entity);
|
||||||
|
|
||||||
|
pd->cache = eina_rbtree_inline_insert(pd->cache, EINA_RBTREE_GET(insert),
|
||||||
|
_cache_tree_cmp, NULL);
|
||||||
|
|
||||||
|
return insert;
|
||||||
|
}
|
||||||
|
|
||||||
static Eina_List *
|
static Eina_List *
|
||||||
_cache_size_fetch(Eina_List *requests, Efl_Ui_Collection_Request **request,
|
_cache_size_fetch(Eina_List *requests, Efl_Ui_Collection_Request **request,
|
||||||
Efl_Ui_Collection_View_Data *pd,
|
Efl_Ui_Collection_View_Data *pd,
|
||||||
|
@ -675,7 +761,7 @@ _cache_size_fetch(Eina_List *requests, Efl_Ui_Collection_Request **request,
|
||||||
Eina_Size2D item_base)
|
Eina_Size2D item_base)
|
||||||
{
|
{
|
||||||
Efl_Ui_Collection_Item_Lookup *lookup;
|
Efl_Ui_Collection_Item_Lookup *lookup;
|
||||||
Efl_Model *model;
|
Efl_Model *model = NULL;
|
||||||
Eina_Size2D item_size = item_base;
|
Eina_Size2D item_size = item_base;
|
||||||
|
|
||||||
if (!pd->cache) goto not_found;
|
if (!pd->cache) goto not_found;
|
||||||
|
@ -683,10 +769,16 @@ _cache_size_fetch(Eina_List *requests, Efl_Ui_Collection_Request **request,
|
||||||
lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
|
lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
|
||||||
sizeof (search_index), _cache_tree_lookup,
|
sizeof (search_index), _cache_tree_lookup,
|
||||||
NULL);
|
NULL);
|
||||||
if (!lookup) goto not_found;
|
|
||||||
|
|
||||||
// In the cache we should always have model, so no need to check for it
|
// In the cache we should always have model, so no need to check for it
|
||||||
model = lookup->item.model;
|
if (lookup)
|
||||||
|
{
|
||||||
|
model = lookup->item.model;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lookup = _build_from_focus(pd, search_index, &model);
|
||||||
|
if (!lookup) goto not_found;
|
||||||
|
}
|
||||||
|
|
||||||
// If we do not know the size
|
// If we do not know the size
|
||||||
if (!ITEM_SIZE_FROM_MODEL(model, item_size))
|
if (!ITEM_SIZE_FROM_MODEL(model, item_size))
|
||||||
|
@ -731,12 +823,14 @@ _cache_entity_fetch(Eina_List *requests, Efl_Ui_Collection_Request **request,
|
||||||
Efl_Ui_Position_Manager_Object_Batch_Entity *target)
|
Efl_Ui_Position_Manager_Object_Batch_Entity *target)
|
||||||
{
|
{
|
||||||
Efl_Ui_Collection_Item_Lookup *lookup;
|
Efl_Ui_Collection_Item_Lookup *lookup;
|
||||||
|
Efl_Model *model = NULL;
|
||||||
|
|
||||||
if (!pd->cache) goto not_found;
|
if (!pd->cache) goto not_found;
|
||||||
|
|
||||||
lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
|
lookup = (void*) eina_rbtree_inline_lookup(pd->cache, &search_index,
|
||||||
sizeof (search_index), _cache_tree_lookup,
|
sizeof (search_index), _cache_tree_lookup,
|
||||||
NULL);
|
NULL);
|
||||||
|
if (!lookup) lookup = _build_from_focus(pd, search_index, &model);
|
||||||
if (!lookup) goto not_found;
|
if (!lookup) goto not_found;
|
||||||
if (!lookup->item.entity) goto not_found;
|
if (!lookup->item.entity) goto not_found;
|
||||||
|
|
||||||
|
@ -1668,8 +1762,27 @@ _efl_ui_collection_view_position_manager_get(const Eo *obj EINA_UNUSED,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_efl_model_count_changed(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
|
_efl_model_count_changed(void *data, const Efl_Event *event EINA_UNUSED)
|
||||||
{
|
{
|
||||||
|
Efl_Ui_Collection_Request *request = NULL;
|
||||||
|
Eina_List *requests = NULL;
|
||||||
|
MY_DATA_GET(data, pd);
|
||||||
|
unsigned int index;
|
||||||
|
unsigned int count = 0;
|
||||||
|
|
||||||
|
count = efl_model_children_count_get(pd->model);
|
||||||
|
if (pd->focus.last)
|
||||||
|
{
|
||||||
|
index = _lookup_entity_index(pd->focus.last, NULL);
|
||||||
|
|
||||||
|
if (index + 1 == count)
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The last item is not the last item anymore
|
||||||
|
requests = _request_add(requests, &request, count, EINA_TRUE);
|
||||||
|
requests = _batch_request_flush(requests, data, pd);
|
||||||
|
|
||||||
// We are not triggering efl_ui_position_manager_entity_data_access_set as it is can
|
// We are not triggering efl_ui_position_manager_entity_data_access_set as it is can
|
||||||
// only be slow, we rely on child added/removed instead (If we were to not rely on
|
// only be slow, we rely on child added/removed instead (If we were to not rely on
|
||||||
// child added/removed we could maybe use count changed)
|
// child added/removed we could maybe use count changed)
|
||||||
|
@ -1863,6 +1976,7 @@ _efl_model_child_removed(void *data, const Efl_Event *event)
|
||||||
if (request_length > 0)
|
if (request_length > 0)
|
||||||
{
|
{
|
||||||
Efl_Ui_Collection_Request *request = NULL;
|
Efl_Ui_Collection_Request *request = NULL;
|
||||||
|
|
||||||
requests = _request_add(requests, &request, ev->index, EINA_TRUE);
|
requests = _request_add(requests, &request, ev->index, EINA_TRUE);
|
||||||
request->length = request_length;
|
request->length = request_length;
|
||||||
requests = eina_list_append(requests, request);
|
requests = eina_list_append(requests, request);
|
||||||
|
@ -1882,9 +1996,7 @@ static void
|
||||||
_efl_ui_collection_view_model_changed(void *data, const Efl_Event *event)
|
_efl_ui_collection_view_model_changed(void *data, const Efl_Event *event)
|
||||||
{
|
{
|
||||||
Efl_Model_Changed_Event *ev = event->info;
|
Efl_Model_Changed_Event *ev = event->info;
|
||||||
#ifdef VIEWPORT_ENABLE
|
|
||||||
Eina_List *requests = NULL;
|
Eina_List *requests = NULL;
|
||||||
#endif
|
|
||||||
MY_DATA_GET(data, pd);
|
MY_DATA_GET(data, pd);
|
||||||
Eina_Iterator *it;
|
Eina_Iterator *it;
|
||||||
const char *property;
|
const char *property;
|
||||||
|
@ -1971,7 +2083,6 @@ _efl_ui_collection_view_model_changed(void *data, const Efl_Event *event)
|
||||||
#ifdef VIEWPORT_ENABLE
|
#ifdef VIEWPORT_ENABLE
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
Efl_Ui_Collection_Request *request;
|
|
||||||
|
|
||||||
if (!pd->viewport[i]) continue ;
|
if (!pd->viewport[i]) continue ;
|
||||||
if (pd->viewport[i]->count == 0) continue ;
|
if (pd->viewport[i]->count == 0) continue ;
|
||||||
|
@ -1986,10 +2097,21 @@ _efl_ui_collection_view_model_changed(void *data, const Efl_Event *event)
|
||||||
|
|
||||||
requests = eina_list_append(requests, request);
|
requests = eina_list_append(requests, request);
|
||||||
}
|
}
|
||||||
requests = _batch_request_flush(requests, data, pd);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch(efl_ui_position_manager_entity_version(pd->manager, 1))
|
// Fetch last item if necessary for later focus
|
||||||
|
if (efl_model_children_count_get(model))
|
||||||
|
{
|
||||||
|
Efl_Ui_Collection_Request *request = NULL;
|
||||||
|
uint64_t index = efl_model_children_count_get(model) - 1;
|
||||||
|
|
||||||
|
requests = _request_add(requests, &request, index, EINA_TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush all pending request
|
||||||
|
requests = _batch_request_flush(requests, data, pd);
|
||||||
|
|
||||||
|
switch (efl_ui_position_manager_entity_version(pd->manager, 1))
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
efl_ui_position_manager_data_access_v1_data_access_set(pd->manager,
|
efl_ui_position_manager_data_access_v1_data_access_set(pd->manager,
|
||||||
|
@ -2249,20 +2371,17 @@ _efl_ui_collection_view_focus_manager_efl_ui_focus_manager_manager_focus_set(Eo
|
||||||
if (focus == efl_ui_focus_manager_root_get(obj))
|
if (focus == efl_ui_focus_manager_root_get(obj))
|
||||||
{
|
{
|
||||||
// Find last item
|
// Find last item
|
||||||
item_id = efl_model_children_count_get(cpd->model) - 1;
|
item = cpd->focus.previously;
|
||||||
|
if (!item) item = cpd->focus.last;
|
||||||
|
if (item) item_id = _lookup_entity_index(item, NULL);
|
||||||
|
else item_id = efl_model_children_count_get(cpd->model) - 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Efl_Model *model;
|
|
||||||
Eina_Value *vindex;
|
|
||||||
|
|
||||||
item = _find_item(obj, cpd, focus);
|
item = _find_item(obj, cpd, focus);
|
||||||
if (!item) return ;
|
if (!item) return ;
|
||||||
|
|
||||||
model = efl_ui_view_model_get(item);
|
item_id = _lookup_entity_index(item, NULL);
|
||||||
vindex = efl_model_property_get(model, "child.index");
|
|
||||||
if (!eina_value_uint_convert(vindex, &item_id)) return;
|
|
||||||
eina_value_free(vindex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is NULL then we are before finalize, we cannot serve any sane value here
|
// If this is NULL then we are before finalize, we cannot serve any sane value here
|
||||||
|
@ -2293,7 +2412,8 @@ EOLIAN static Efl_Ui_Focus_Object *
|
||||||
_efl_ui_collection_view_focus_manager_efl_ui_focus_manager_request_move(Eo *obj, Efl_Ui_Collection_View_Focus_Manager_Data *pd, Efl_Ui_Focus_Direction direction, Efl_Ui_Focus_Object *child, Eina_Bool logical)
|
_efl_ui_collection_view_focus_manager_efl_ui_focus_manager_request_move(Eo *obj, Efl_Ui_Collection_View_Focus_Manager_Data *pd, Efl_Ui_Focus_Direction direction, Efl_Ui_Focus_Object *child, Eina_Bool logical)
|
||||||
{
|
{
|
||||||
MY_DATA_GET(pd->collection, cpd);
|
MY_DATA_GET(pd->collection, cpd);
|
||||||
Efl_Ui_Item *new_item, *item;
|
Efl_Ui_Item *new_item = NULL;
|
||||||
|
Efl_Ui_Item *item;
|
||||||
unsigned int item_id;
|
unsigned int item_id;
|
||||||
|
|
||||||
if (!child)
|
if (!child)
|
||||||
|
@ -2302,11 +2422,11 @@ _efl_ui_collection_view_focus_manager_efl_ui_focus_manager_request_move(Eo *obj,
|
||||||
item = _find_item(obj, cpd, child);
|
item = _find_item(obj, cpd, child);
|
||||||
|
|
||||||
//if this is NULL then we are before finalize, we cannot serve any sane value here
|
//if this is NULL then we are before finalize, we cannot serve any sane value here
|
||||||
if (!cpd->manager) return NULL;
|
if (!cpd->manager) goto end;
|
||||||
if (!item) return NULL;
|
if (!item) goto end;
|
||||||
|
|
||||||
if (!_id_from_item(item, &item_id))
|
if (!_id_from_item(item, &item_id))
|
||||||
return NULL;
|
goto end;
|
||||||
|
|
||||||
if (ITEM_IS_OUTSIDE_VISIBLE(item_id))
|
if (ITEM_IS_OUTSIDE_VISIBLE(item_id))
|
||||||
{
|
{
|
||||||
|
@ -2361,6 +2481,8 @@ _efl_ui_collection_view_focus_manager_efl_ui_focus_manager_request_move(Eo *obj,
|
||||||
new_item = efl_ui_focus_manager_request_move(efl_super(obj, EFL_UI_COLLECTION_VIEW_FOCUS_MANAGER_CLASS), direction, child, logical);
|
new_item = efl_ui_focus_manager_request_move(efl_super(obj, EFL_UI_COLLECTION_VIEW_FOCUS_MANAGER_CLASS), direction, child, logical);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
efl_replace(&cpd->focus.previously, new_item);
|
||||||
return new_item;
|
return new_item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue