efl_ui_position_manager_grid: start to honor group items

This commit introduces the correct placement of group items and normal
items. The gruop items are also floating on the top of there child items
in case they are not visible on theire own. Items without group items
between items with groups are right now a little bit troublesome and
might display the wrong group, we *need* to check later on if this case
is even needed or not.

The whole placement code now uses 2 different caches, one cache is
counting how many groups we have, and how many items each group has.
Additionally, the size of the header + the state of the header is
safes. The second cache does translate that into how much size one full
group needs on the screen to be placed, this makes the calculation of
the correct item placement a lot faster.
The invalidation of the caches is also quite good. The size cache only
depends on the viewport size and the group cache, which means its
*never* recaclulated on a normal scroll operation. Only if items are
added, or the widget is resized (The later case can also be more
optimized). The group cache is only invalidated when new items are
added (Which is normally not happening during rendering)

ref T8115

Reviewed-by: Cedric BAIL <cedric.bail@free.fr>
Differential Revision: https://phab.enlightenment.org/D9608
This commit is contained in:
Marcel Hollerbach 2019-08-17 14:32:36 +02:00
parent 58c3e676e8
commit 3cb3c1c4b1
1 changed files with 519 additions and 103 deletions

View File

@ -12,37 +12,432 @@
#define MY_DATA_GET(obj, pd) \
Efl_Ui_Position_Manager_Grid_Data *pd = efl_data_scope_get(obj, MY_CLASS);
typedef struct {
unsigned int start_id, end_id;
} Vis_Segment;
typedef struct {
Api_Callback min_size, object;
unsigned int size;
unsigned int groups;
Eina_Rect viewport;
Eina_Vector2 scroll_position;
Efl_Ui_Layout_Orientation dir;
struct {
unsigned int start_id, end_id;
} prev_run;
Vis_Segment prev_run;
Eina_Size2D max_min_size;
Eina_Size2D last_viewport_size;
Eina_Size2D prev_min_size;
struct {
int columns;
int rows;
} current_display_table;
Eina_Inarray *group_cache;
Eina_Bool group_cache_dirty;
int *size_cache;
Eina_Bool size_cache_dirty;
Eo *last_group;
Eina_Future *rebuild_absolut_size;
} Efl_Ui_Position_Manager_Grid_Data;
typedef struct {
Eina_Bool real_group;
Eina_Size2D group_header_size;
int items;
} Group_Cache_Line;
static void
_group_cache_require(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
{
unsigned int i;
const int len = 100;
Efl_Ui_Position_Manager_Batch_Size_Access size_buffer[len];
Group_Cache_Line line = { 0 };
if (!pd->group_cache_dirty)
return;
pd->group_cache_dirty = EINA_FALSE;
if (pd->group_cache)
eina_inarray_free(pd->group_cache);
pd->group_cache = eina_inarray_new(sizeof(Group_Cache_Line), 10);
for (i = 0; i < pd->size; ++i)
{
int buffer_id = i % len;
if (buffer_id == 0)
{
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->min_size, i, len, NULL, size_buffer) > 0);
}
if (size_buffer[buffer_id].group == EFL_UI_POSITION_MANAGER_BATCH_GROUP_STATE_GROUP)
{
eina_inarray_push(pd->group_cache, &line);
line.real_group = EINA_TRUE;
line.group_header_size = size_buffer[buffer_id].size;
line.items = 1;
}
else if (size_buffer[buffer_id].group == EFL_UI_POSITION_MANAGER_BATCH_GROUP_STATE_PART_OF_GROUP ||
(!line.real_group && size_buffer[buffer_id].group == EFL_UI_POSITION_MANAGER_BATCH_GROUP_STATE_NO_GROUP))
{
line.items ++;
}
else if (size_buffer[buffer_id].group == EFL_UI_POSITION_MANAGER_BATCH_GROUP_STATE_NO_GROUP && line.real_group)
{
eina_inarray_push(pd->group_cache, &line);
line.real_group = EINA_FALSE;
line.group_header_size = EINA_SIZE2D(0, 0);
line.items = 0;
}
}
eina_inarray_push(pd->group_cache, &line);
}
static inline void
_group_cache_invalidate(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
{
pd->group_cache_dirty = EINA_TRUE;
pd->size_cache_dirty = EINA_TRUE;
}
static void
_size_cache_require(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
{
if (!pd->size_cache_dirty) return;
_group_cache_require(obj, pd);
pd->size_cache_dirty = EINA_FALSE;
if (pd->size_cache)
free(pd->size_cache);
pd->size_cache = calloc(sizeof(int), eina_inarray_count(pd->group_cache));
for (unsigned int i = 0; i < eina_inarray_count(pd->group_cache); ++i)
{
Group_Cache_Line *line = eina_inarray_nth(pd->group_cache, i);
int header_out = 0;
if (line->real_group)
header_out = 1;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
pd->size_cache[i] = line->group_header_size.h +
(ceil(
(double)(line->items - header_out)/ /* the number of real items in the group (- the group item) */
(int)(pd->viewport.w/pd->max_min_size.w))) /* devided by the number of items per row */
*pd->max_min_size.h;
else
pd->size_cache[i] = (ceil((double)(line->items - header_out)/
(int)((pd->viewport.h-line->group_header_size.h)/pd->max_min_size.h)))*pd->max_min_size.w;
}
}
static inline void
_size_cache_invalidate(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
{
pd->size_cache_dirty = EINA_TRUE;
}
typedef struct {
int resulting_id;
int consumed_space;
} Search_Result;
static inline Search_Result
_search_id(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, int relevant_space_size)
{
int consumed_space = 0;
int consumed_groups = -1;
int consumed_ids = 0;
int sub_ids = 0;
Search_Result res;
//first we search how many blocks we can skip
for (unsigned int i = 0; i < eina_inarray_count(pd->group_cache); ++i)
{
Group_Cache_Line *line = eina_inarray_nth(pd->group_cache, i);
if (consumed_space + pd->size_cache[i] > relevant_space_size)
break;
consumed_space += pd->size_cache[i];
consumed_groups = i;
consumed_ids += line->items;
}
Group_Cache_Line *line = NULL;
if (consumed_groups > -1 && consumed_groups + 1 < (int)eina_inarray_count(pd->group_cache))
line = eina_inarray_nth(pd->group_cache, consumed_groups + 1);
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
//now we have relevant_space_size - consumed_space left maybe we are searching the group item
if (line && line->real_group)
{
if (consumed_space + line->group_header_size.h > relevant_space_size)
{
res.resulting_id = consumed_ids;
res.consumed_space = consumed_space;
return res;
}
else
{
consumed_space += line->group_header_size.h;
consumed_ids += 1;
}
}
//now we need to locate at which id we are starting
int space_top = relevant_space_size - consumed_space;
consumed_space += floor(space_top/pd->max_min_size.h)*pd->max_min_size.h;
sub_ids = floor(space_top/pd->max_min_size.h)*(pd->viewport.w/pd->max_min_size.w);
}
else
{
int header_height = 0;
if (line && line->real_group)
{
header_height = line->group_header_size.h;
}
//now we need to locate at which id we are starting
const int space_left = relevant_space_size - consumed_space;
consumed_space += floor(space_left/pd->max_min_size.w)*pd->max_min_size.w;
sub_ids = floor(space_left/pd->max_min_size.w)*((pd->viewport.h-header_height)/pd->max_min_size.h);
if (line && line->real_group &&
sub_ids > 0) /* if we are in the first row, we need the group item to be visible, otherwise, we need to add that to the consumed ids */
{
sub_ids += 1;
}
}
res.resulting_id = consumed_ids + sub_ids;
res.consumed_space = consumed_space;
return res;
}
static inline Eina_Bool
_search_start_end(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, int relevant_viewport, int relevant_space_size, unsigned int step, Vis_Segment *cur, int *consumed_space)
{
Search_Result start = _search_id(obj, pd, MAX(relevant_space_size, 0));
Search_Result end = _search_id(obj, pd, MAX(relevant_space_size, 0)+relevant_viewport+step*2);
cur->start_id = MIN(MAX(start.resulting_id, 0), (int)pd->size);
cur->end_id = MAX(MIN(end.resulting_id, (int)pd->size), 0);
*consumed_space = start.consumed_space;
return EINA_TRUE;
}
typedef struct {
int relevant_space_size;
int consumed_space;
Vis_Segment new;
Eo *floating_group;
Eina_Size2D floating_size;
Eo *placed_item;
} Item_Position_Context;
static inline void
_place_grid_item(Eina_Rect *geom, Efl_Ui_Position_Manager_Grid_Data *pd, int x, int y)
{
geom->x += x*pd->max_min_size.w;
geom->y += y*pd->max_min_size.h;
geom->size = pd->max_min_size;
}
static inline void
_position_items_vertical(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, Item_Position_Context *ctx)
{
Eina_Position2D start = pd->viewport.pos;
unsigned int i;
const int len = 100;
int columns, last_block_start = ctx->new.start_id;
Efl_Ui_Position_Manager_Batch_Entity_Access obj_buffer[len];
if (!pd->viewport.w || !pd->viewport.h) return;
start.y -= (ctx->relevant_space_size - ctx->consumed_space);
columns = pd->viewport.w/pd->max_min_size.w;
for (i = ctx->new.start_id; i < ctx->new.end_id; ++i)
{
int buffer_id = (i-ctx->new.start_id) % len;
if (buffer_id == 0)
{
int tmp_group;
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->object, i, len, &tmp_group, obj_buffer) > 0);
if (tmp_group != -1 && i == ctx->new.start_id)
{
Efl_Ui_Position_Manager_Batch_Size_Access size_buffer[1];
Efl_Ui_Position_Manager_Batch_Entity_Access obj_buffer[1];
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->min_size, tmp_group, 1, NULL, size_buffer) != 0);
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->object, tmp_group, 1, NULL, obj_buffer) != 0);
ctx->floating_group = obj_buffer[0].entity;
ctx->floating_size.h = size_buffer[0].size.h;
ctx->floating_size.w = pd->viewport.w;
}
}
Eina_Rect geom;
geom.pos = start;
int x = (i - last_block_start)%columns;
int y = (i - last_block_start)/columns;
if (obj_buffer[buffer_id].entity == pd->last_group)
pd->last_group = NULL;
if (obj_buffer[buffer_id].group == EFL_UI_POSITION_MANAGER_BATCH_GROUP_STATE_GROUP)
{
Efl_Ui_Position_Manager_Batch_Size_Access size_buffer[1];
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->min_size, i, 1, NULL, size_buffer) == 1);
if (x != 0)
y += 1;
last_block_start = i + 1;
start.y += size_buffer[0].size.h + y*pd->max_min_size.h;
geom.size = pd->viewport.size;
geom.h = size_buffer[0].size.h;
geom.y += y*pd->max_min_size.h;
if (!ctx->placed_item)
ctx->placed_item = obj_buffer[buffer_id].entity;
}
else
{
_place_grid_item(&geom, pd, x, y);
}
efl_gfx_entity_geometry_set(obj_buffer[buffer_id].entity, geom);
}
}
static inline void
_position_items_horizontal(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, Item_Position_Context *ctx)
{
Eina_Position2D start = pd->viewport.pos;
unsigned int i;
const int len = 100;
int columns, last_block_start = ctx->new.start_id;
Efl_Ui_Position_Manager_Batch_Entity_Access obj_buffer[len];
if (!pd->viewport.w || !pd->viewport.h) return;
start.x -= (ctx->relevant_space_size - ctx->consumed_space);
columns = (pd->viewport.h)/pd->max_min_size.h;
for (i = ctx->new.start_id; i < ctx->new.end_id; ++i)
{
int buffer_id = (i-ctx->new.start_id) % len;
if (buffer_id == 0)
{
int tmp_group;
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->object, i, len, &tmp_group, obj_buffer) > 0);
if (tmp_group != -1 && i == ctx->new.start_id && pd->dir != EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
Efl_Ui_Position_Manager_Batch_Size_Access size_buffer[1];
Efl_Ui_Position_Manager_Batch_Entity_Access obj_buffer[1];
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->min_size, tmp_group, 1, NULL, size_buffer) != 0);
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->object, tmp_group, 1, NULL, obj_buffer) != 0);
start.y += size_buffer[0].size.h;
columns = (pd->viewport.h - size_buffer[0].size.h)/pd->max_min_size.h;
ctx->floating_group = obj_buffer[0].entity;
ctx->floating_size.h = size_buffer[0].size.h;
ctx->floating_size.w = pd->viewport.w;
}
}
Eina_Rect geom;
geom.pos = start;
int x = (i - last_block_start)/columns;
int y = (i - last_block_start)%columns;
if (obj_buffer[buffer_id].entity == pd->last_group)
pd->last_group = NULL;
if (obj_buffer[buffer_id].group == EFL_UI_POSITION_MANAGER_BATCH_GROUP_STATE_GROUP)
{
Efl_Ui_Position_Manager_Batch_Size_Access size_buffer[1];
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->min_size, i, 1, NULL, size_buffer) == 1);
last_block_start = i + 1;
start.y = pd->viewport.y + size_buffer[0].size.h;
start.x += x*pd->max_min_size.w;
geom.size.h = size_buffer[0].size.h;
geom.size.w = pd->viewport.w;
geom.x += x*pd->max_min_size.w;
geom.y = pd->viewport.y;
columns = (pd->viewport.h - size_buffer[0].size.h)/pd->max_min_size.h;
if (!ctx->placed_item)
ctx->placed_item = obj_buffer[buffer_id].entity;
}
else
{
_place_grid_item(&geom, pd, x, y);
}
efl_gfx_entity_geometry_set(obj_buffer[buffer_id].entity, geom);
}
}
static inline void
_position_group_items(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, Item_Position_Context *ctx)
{
//floating group is not yet positioned, in case it is there, we need to position it there
Eina_Rect geom;
if (!ctx->floating_group && pd->last_group)
{
efl_gfx_entity_visible_set(pd->last_group, EINA_FALSE);
pd->last_group = NULL;
}
if (ctx->floating_group)
{
geom.pos = pd->viewport.pos;
geom.size = ctx->floating_size;
if (ctx->placed_item)
{
Eina_Rect placed = efl_gfx_entity_geometry_get(ctx->placed_item);
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
geom.y = MIN(geom.y, placed.y-geom.h);
}
else
{
geom.x = MIN(geom.x, placed.x-geom.w);
}
}
if (pd->last_group != ctx->floating_group)
{
efl_gfx_entity_visible_set(pd->last_group, EINA_FALSE);
pd->last_group = ctx->floating_group;
}
efl_gfx_entity_visible_set(ctx->floating_group, EINA_TRUE);
efl_gfx_stack_raise_to_top(ctx->floating_group);
efl_gfx_entity_geometry_set(ctx->floating_group, geom);
}
else if (ctx->placed_item)
{
Eina_Rect placed = efl_gfx_entity_geometry_get(ctx->placed_item);
placed.x = MAX(placed.x, pd->viewport.x);
placed.y = MAX(placed.y, pd->viewport.y);
efl_gfx_entity_geometry_set(ctx->placed_item, placed);
efl_gfx_stack_raise_to_top(ctx->placed_item);
}
}
static void
_reposition_content(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
{
Eina_Size2D space_size;
int relevant_space_size, relevant_viewport;
unsigned int start_id, end_id, step;
const int len = 100;
Efl_Ui_Position_Manager_Batch_Entity_Access obj_buffer[len];
int relevant_space_size, relevant_viewport, consumed_space;
Vis_Segment cur;
unsigned int step;
Efl_Ui_Position_Manager_Range_Update ev;
Item_Position_Context ctx;
if (!pd->size) return;
if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return;
if (pd->current_display_table.columns <= 0 || pd->current_display_table.rows <= 0) return;
if (!eina_inarray_count(pd->group_cache)) return;
//space size contains the amount of space that is outside the viewport (either to the top or to the left)
space_size.w = (MAX(pd->last_viewport_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
@ -60,62 +455,45 @@ _reposition_content(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
relevant_viewport = pd->viewport.w;
step = pd->max_min_size.w;
}
start_id = MIN((MAX(relevant_space_size,0) / step)*pd->current_display_table.columns, pd->size);
end_id = MIN((((MAX(relevant_space_size,0) + relevant_viewport + step) / step)*pd->current_display_table.columns)+1, pd->size);
EINA_SAFETY_ON_FALSE_RETURN(start_id <= end_id);
EINA_SAFETY_ON_FALSE_RETURN(start_id <= pd->size);
if (!_search_start_end(obj, pd, relevant_viewport, relevant_space_size, step, &cur, &consumed_space))
return;
//to performance optimize the whole widget, we are setting the objects that are outside the viewport to visibility false
//The code below ensures that things outside the viewport are always hidden, and things inside the viewport are visible
if (end_id < pd->prev_run.start_id || start_id > pd->prev_run.end_id)
if (cur.end_id < pd->prev_run.start_id || cur.start_id > pd->prev_run.end_id)
{
//it is important to first make the segment visible here, and then hide the rest
//otherwise we get a state where item_container has 0 subchildren, which triggers a lot of focus logic.
vis_change_segment(&pd->object, start_id, end_id, EINA_TRUE);
vis_change_segment(&pd->object, cur.start_id, cur.end_id, EINA_TRUE);
vis_change_segment(&pd->object, pd->prev_run.start_id, pd->prev_run.end_id, EINA_FALSE);
}
else
{
vis_change_segment(&pd->object, pd->prev_run.start_id, start_id, (pd->prev_run.start_id > start_id));
vis_change_segment(&pd->object, pd->prev_run.end_id, end_id, (pd->prev_run.end_id < end_id));
vis_change_segment(&pd->object, pd->prev_run.start_id, cur.start_id, (pd->prev_run.start_id > cur.start_id));
vis_change_segment(&pd->object, pd->prev_run.end_id, cur.end_id, (pd->prev_run.end_id < cur.end_id));
}
for (unsigned int i = start_id; i < end_id; ++i)
ctx.new = cur;
ctx.consumed_space = consumed_space;
ctx.relevant_space_size = relevant_space_size;
ctx.floating_group = NULL;
ctx.placed_item = NULL;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
Eina_Rect geom;
Efl_Gfx_Entity *ent;
int buffer_id = (i-start_id) % len;
geom.size = pd->max_min_size;
geom.pos = pd->viewport.pos;
if (buffer_id == 0)
{
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->object, i, len, NULL, obj_buffer) > 0);
}
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
geom.x += pd->max_min_size.w*(i%pd->current_display_table.columns);
geom.y += pd->max_min_size.h*(i/pd->current_display_table.columns);
geom.y -= (relevant_space_size);
}
else
{
geom.x += pd->max_min_size.w*(i/pd->current_display_table.columns);
geom.y += pd->max_min_size.h*(i%pd->current_display_table.columns);
geom.x -= (relevant_space_size);
}
ent = obj_buffer[buffer_id].entity;
//printf(">%d (%d, %d, %d, %d) %p\n", i, geom.x, geom.y, geom.w, geom.h, ent);
efl_gfx_entity_geometry_set(ent, geom);
_position_items_vertical(obj, pd, &ctx);
_position_group_items(obj, pd, &ctx);
}
if (pd->prev_run.start_id != start_id || pd->prev_run.end_id != end_id)
else
{
ev.start_id = pd->prev_run.start_id = start_id;
ev.end_id = pd->prev_run.end_id = end_id;
_position_items_horizontal(obj, pd, &ctx);
_position_group_items(obj, pd, &ctx);
}
if (pd->prev_run.start_id != cur.start_id || pd->prev_run.end_id != cur.end_id)
{
ev.start_id = pd->prev_run.start_id = cur.start_id;
ev.end_id = pd->prev_run.end_id = cur.end_id;
efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_VISIBLE_RANGE_CHANGED, &ev);
}
}
@ -123,44 +501,27 @@ _reposition_content(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd)
static inline void
_flush_abs_size(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd)
{
int minor, major;
Eina_Size2D vp_size;
int sum_of_cache = 0;
if (!pd->size) return;
if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
_size_cache_require(obj, pd);
for (unsigned int i = 0; i < eina_inarray_count(pd->group_cache); ++i)
{
major = pd->viewport.w/pd->max_min_size.w;
pd->current_display_table.columns = major;
}
else
{
major = pd->viewport.h/pd->max_min_size.h;
pd->current_display_table.columns = major;
sum_of_cache += pd->size_cache[i];
}
if (major <= 0) return;
minor = ceil((double)pd->size/(double)major);
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
pd->current_display_table.rows = minor;
else
pd->current_display_table.rows = minor;
/*
* calculate how much size we need with major in the given orientation.
* The
*/
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
vp_size.w = pd->viewport.w;
vp_size.h = minor*pd->max_min_size.h;
vp_size.h = sum_of_cache;
}
else
{
vp_size.h = pd->viewport.h;
vp_size.w = minor*pd->max_min_size.w;
vp_size.w = sum_of_cache;
}
if (vp_size.h != pd->last_viewport_size.h || vp_size.w != pd->last_viewport_size.w)
{
@ -208,6 +569,7 @@ _efl_ui_position_manager_grid_efl_ui_position_manager_entity_data_access_set(Eo
EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_viewport_set(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, Eina_Rect viewport)
{
_size_cache_invalidate(obj, pd);
pd->viewport = viewport;
_flush_abs_size(obj, pd);
_reposition_content(obj, pd);
@ -221,6 +583,29 @@ _efl_ui_position_manager_grid_efl_ui_position_manager_entity_scroll_position_set
_reposition_content(obj, pd);
}
static Eina_Value
_rebuild_job_cb(void *data, Eina_Value v EINA_UNUSED, const Eina_Future *f EINA_UNUSED)
{
MY_DATA_GET(data, pd);
if (!efl_alive_get(data)) return EINA_VALUE_EMPTY;
_flush_abs_size(data, pd);
_reposition_content(data, pd);
pd->rebuild_absolut_size = NULL;
return EINA_VALUE_EMPTY;
}
static void
_schedule_recalc_abs_size(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd)
{
if (pd->rebuild_absolut_size) return;
pd->rebuild_absolut_size = efl_loop_job(efl_app_main_get());
eina_future_then(pd->rebuild_absolut_size, _rebuild_job_cb, obj);
}
EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_item_added(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd, int added_index, Efl_Gfx_Entity *subobj EINA_UNUSED)
{
@ -228,27 +613,26 @@ _efl_ui_position_manager_grid_efl_ui_position_manager_entity_item_added(Eo *obj,
pd->size ++;
efl_gfx_entity_visible_set(subobj, EINA_FALSE);
_group_cache_invalidate(obj, pd);
EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->min_size, added_index, 1, NULL, &size) == 1);
_update_min_size(obj, pd, added_index, size[0].size);
_flush_min_size(obj, pd);
_flush_abs_size(obj, pd);
_reposition_content(obj, pd); //FIXME we might can skip that
_schedule_recalc_abs_size(obj, pd);
}
EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_item_removed(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_Grid_Data *pd, int removed_index EINA_UNUSED, Efl_Gfx_Entity *subobj EINA_UNUSED)
{
//we ignore here that we might loose the item giving the current max min size
EINA_SAFETY_ON_FALSE_RETURN(pd->size > 0);
pd->size --;
_group_cache_invalidate(obj, pd);
pd->prev_run.start_id = MIN(pd->prev_run.start_id, pd->size);
pd->prev_run.end_id = MIN(pd->prev_run.end_id, pd->size);
//we ignore here that we might loose the item giving the current max min size
_flush_abs_size(obj, pd);
_reposition_content(obj, pd); //FIXME we might can skip that
_schedule_recalc_abs_size(obj, pd);
efl_gfx_entity_visible_set(subobj, EINA_TRUE);
}
EOLIAN static void
_efl_ui_position_manager_grid_efl_ui_position_manager_entity_item_size_changed(Eo *obj, Efl_Ui_Position_Manager_Grid_Data *pd, int start_id, int end_id)
{
@ -264,10 +648,9 @@ _efl_ui_position_manager_grid_efl_ui_position_manager_entity_item_size_changed(E
}
_update_min_size(obj, pd, i, data[i-start_id].size);
}
_size_cache_invalidate(obj, pd);
_flush_min_size(obj, pd);
_flush_abs_size(obj, pd);
_reposition_content(obj, pd); //FIXME we could check if this is needed or not
_schedule_recalc_abs_size(obj, pd);
}
EOLIAN static void
@ -292,38 +675,71 @@ _efl_ui_position_manager_grid_efl_ui_position_manager_entity_position_single_ite
Eina_Rect geom;
Eina_Size2D space_size;
unsigned int relevant_space_size;
unsigned int group_consumed_size = 0;
unsigned int group_consumed_ids = 0;
Efl_Ui_Position_Manager_Batch_Size_Access size_buffer[1];
if (!pd->size) return EINA_RECT(0, 0, 0, 0);
if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return EINA_RECT(0, 0, 0, 0);
if (pd->current_display_table.columns <= 0 || pd->current_display_table.rows <= 0) return EINA_RECT(0, 0, 0, 0);
EINA_SAFETY_ON_FALSE_RETURN_VAL(_fill_buffer(&pd->min_size, idx, 1, NULL, size_buffer) == 1, EINA_RECT_EMPTY());
_size_cache_require(obj, pd);
_flush_abs_size(obj, pd);
//space size contains the amount of space that is outside the viewport (either to the top or to the left)
space_size.w = (MAX(pd->last_viewport_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
space_size.h = (MAX(pd->last_viewport_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
EINA_SAFETY_ON_FALSE_RETURN_VAL(space_size.w >= 0 && space_size.h >= 0, EINA_RECT(0, 0, 0, 0));
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
relevant_space_size = space_size.h;
}
relevant_space_size = space_size.h;
else
{
relevant_space_size = space_size.w;
}
relevant_space_size = space_size.w;
geom.size = pd->max_min_size;
geom.pos = pd->viewport.pos;
for (unsigned int i = 0; i < eina_inarray_count(pd->group_cache); ++i)
{
Group_Cache_Line *line = eina_inarray_nth(pd->group_cache, i);
if ((int)group_consumed_ids + line->items > idx)
break;
group_consumed_size += pd->size_cache[i];
group_consumed_ids += line->items;
if (line->real_group && idx == (int)group_consumed_ids + 1)
{
geom.y = (relevant_space_size - group_consumed_size);
geom.size = size_buffer[0].size;
return geom;
}
else if (line->real_group)
group_consumed_size += line->group_header_size.h;
}
if (idx > 0)
EINA_SAFETY_ON_FALSE_RETURN_VAL(group_consumed_ids < (unsigned int)idx, EINA_RECT(0, 0, 0, 0));
else if (idx == 0)
EINA_SAFETY_ON_FALSE_RETURN_VAL(group_consumed_ids == 0, EINA_RECT(0, 0, 0, 0));
int columns = pd->viewport.w/pd->max_min_size.w;
int sub_pos_id = idx - group_consumed_ids;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
geom.x += pd->max_min_size.w*(idx%pd->current_display_table.columns);
geom.y += pd->max_min_size.h*(idx/pd->current_display_table.columns);
geom.y -= (relevant_space_size);
const int x = (sub_pos_id)%columns;
const int y = (sub_pos_id)/columns;
geom.y -= relevant_space_size;
geom.x += pd->max_min_size.w*x;
geom.y += group_consumed_size + pd->max_min_size.h*y;
}
else
{
geom.x += pd->max_min_size.w*(idx/pd->current_display_table.columns);
geom.y += pd->max_min_size.h*(idx%pd->current_display_table.columns);
geom.x -= (relevant_space_size);
const int x = (sub_pos_id)/columns;
const int y = (sub_pos_id)%columns;
geom.x -= relevant_space_size;
geom.y += pd->max_min_size.h*y;
geom.x += group_consumed_size + pd->max_min_size.w*x;
}
return geom;
@ -344,10 +760,10 @@ _efl_ui_position_manager_grid_efl_ui_position_manager_entity_relative_item(Eo *o
new_id -= 1;
break;
case EFL_UI_FOCUS_DIRECTION_UP:
new_id -= pd->current_display_table.columns;
//FIXME
break;
case EFL_UI_FOCUS_DIRECTION_DOWN:
new_id += pd->current_display_table.columns;
//FIXME
break;
default:
new_id = -1;