efl/src/lib/elementary/efl_ui_table_layout.c

432 lines
14 KiB
C

#include "efl_ui_table_private.h"
#include "efl_ui_container_layout.h"
typedef struct _Item_Calc Item_Calc;
typedef struct _Cell_Calc Cell_Calc;
typedef struct _Table_Calc Table_Calc;
struct _Item_Calc
{
Evas_Object *obj;
int cell_span[2];
int cell_index[2];
Efl_Ui_Container_Item_Hints hints[2]; /* 0 is x-axis, 1 is y-axis */
};
struct _Cell_Calc
{
EINA_INLIST;
int index;
int next;
double acc;
double space;
double weight;
double weight_factor;
Eina_Bool occupied : 1;
};
struct _Table_Calc
{
/* 0 is x-axis, 1 is y-axis */
int rows;
int cols;
int want[2];
int hgsize[2];
double weight_sum[2];
Cell_Calc *cell_calc[2];
Efl_Ui_Container_Layout_Calc layout_calc[2];
};
static int
_weight_sort_cb(const void *l1, const void *l2)
{
Cell_Calc *cc1, *cc2;
cc1 = EINA_INLIST_CONTAINER_GET(l1, Cell_Calc);
cc2 = EINA_INLIST_CONTAINER_GET(l2, Cell_Calc);
return cc2->weight_factor <= cc1->weight_factor ? -1 : 1;
}
static void
_cell_weight_calc(Table_Calc *table_calc, Eina_Bool axis)
{
int i, count, layout_size, calc_size;
double denom, weight_sum, calc_weight;
Eina_Inlist *inlist = NULL;
Cell_Calc *cell_calc, *cc;
layout_size = calc_size = table_calc->layout_calc[axis].size;
weight_sum = calc_weight = table_calc->weight_sum[axis];
cell_calc = table_calc->cell_calc[axis];
count = axis ? table_calc->rows : table_calc->cols;
for (i = 0; i < count; i = cell_calc[i].next)
{
denom = (cell_calc[i].weight * layout_size) -
(weight_sum * cell_calc[i].space);
if (denom > 0)
{
cell_calc[i].weight_factor = (cell_calc[i].weight * layout_size) / denom;
inlist = eina_inlist_sorted_insert(inlist,
EINA_INLIST_GET(&cell_calc[i]),
_weight_sort_cb);
}
else
{
calc_size -= cell_calc[i].space;
calc_weight -= cell_calc[i].weight;
}
}
EINA_INLIST_FOREACH(inlist, cc)
{
double weight_len;
weight_len = (calc_size * cc->weight) / calc_weight;
if (cc->space < weight_len)
{
cc->space = weight_len;
}
else
{
calc_size -= cc->space;
calc_weight -= cc->weight;
}
}
}
static void
_efl_ui_table_homogeneous_cell_init(Table_Calc *table_calc, Eina_Bool axis)
{
int i, index = 0, mmin = 0, count;
Cell_Calc *prev_cell = NULL, *cell_calc;
cell_calc = table_calc->cell_calc[axis];
count = axis ? table_calc->rows : table_calc->cols;
for (i = 0; i < count; i++)
{
if (!cell_calc[i].occupied) continue;
cell_calc[i].index = index++;
if (cell_calc[i].space > mmin)
mmin = cell_calc[i].space;
if (prev_cell)
prev_cell->next = i;
prev_cell = &cell_calc[i];
}
if (!index)
{
memset(table_calc, 0, sizeof(Table_Calc));
return;
}
if (prev_cell)
prev_cell->next = count;
table_calc->layout_calc[axis].size -= (table_calc->layout_calc[axis].pad
* (index - 1));
table_calc->want[axis] = mmin * index;
table_calc->weight_sum[axis] = index;
if (table_calc->want[axis] > table_calc->layout_calc[axis].size)
table_calc->hgsize[axis] = table_calc->want[axis] / index;
else
table_calc->hgsize[axis] = table_calc->layout_calc[axis].size / index;
table_calc->hgsize[axis] += table_calc->layout_calc[axis].pad;
}
static void
_efl_ui_table_regular_cell_init(Table_Calc *table_calc, Eina_Bool axis)
{
int i, index = 0, acc, want = 0, count;
double weight_sum = 0;
Cell_Calc *prev_cell = NULL, *cell_calc;
Efl_Ui_Container_Layout_Calc *layout_calc;
layout_calc = &(table_calc->layout_calc[axis]);
cell_calc = table_calc->cell_calc[axis];
count = axis ? table_calc->rows : table_calc->cols;
for (i = 0; i < count; i++)
{
if (!cell_calc[i].occupied) continue;
else if (i && cell_calc[0].next == 0) cell_calc[0].next = i;
cell_calc[i].index = index++;
want += cell_calc[i].space;
weight_sum += cell_calc[i].weight;
if (prev_cell)
prev_cell->next = i;
prev_cell = &cell_calc[i];
}
if (prev_cell)
prev_cell->next = count;
table_calc->want[axis] = want;
table_calc->weight_sum[axis] = weight_sum;
table_calc->layout_calc[axis].size -= (table_calc->layout_calc[axis].pad
* (index - 1));
if ((layout_calc->size > want) && (weight_sum > 0))
_cell_weight_calc(table_calc, axis);
if (EINA_DBL_EQ(weight_sum, 0.0))
layout_calc->pos += (layout_calc->size - want) * layout_calc->align;
for (i = 0, acc = 0; i < count; acc += cell_calc[i].space, i = cell_calc[i].next)
cell_calc[i].acc = acc;
}
static inline int
_efl_ui_table_homogeneous_item_pos_get(Table_Calc *table_calc, Item_Calc *item, Eina_Bool axis)
{
return 0.5 + table_calc->layout_calc[axis].pos + (table_calc->hgsize[axis]
* table_calc->cell_calc[axis][item->cell_index[axis]].index);
}
static inline int
_efl_ui_table_homogeneous_item_size_get(Table_Calc *table_calc, Item_Calc *item, Eina_Bool axis)
{
return (table_calc->hgsize[axis] * item->cell_span[axis])
- table_calc->layout_calc[axis].pad;
}
static inline int
_efl_ui_table_regular_item_pos_get(Table_Calc *table_calc, Item_Calc *item, Eina_Bool axis)
{
return 0.5 + table_calc->layout_calc[axis].pos
+ table_calc->cell_calc[axis][item->cell_index[axis]].acc
+ (table_calc->cell_calc[axis][item->cell_index[axis]].index *
table_calc->layout_calc[axis].pad);
}
static inline int
_efl_ui_table_regular_item_size_get(Table_Calc *table_calc, Item_Calc *item, Eina_Bool axis)
{
int start, end;
start = item->cell_index[axis];
end = start + item->cell_span[axis] - 1;
return table_calc->cell_calc[axis][end].acc
- table_calc->cell_calc[axis][start].acc
+ table_calc->cell_calc[axis][end].space
+ ((item->cell_span[axis] - 1) * table_calc->layout_calc[axis].pad)
- item->hints[axis].margin[0] - item->hints[axis].margin[1];
}
/* this function performs a simplified layout when the table has changed position
* but no other changes have occurred, e.g., when a table is being scrolled
*/
static void
_efl_ui_table_layout_simple(Efl_Ui_Table *ui_table, Efl_Ui_Table_Data *pd)
{
Table_Item *ti;
Eina_Position2D pos = efl_gfx_entity_position_get(ui_table);
EINA_INLIST_FOREACH(EINA_INLIST_GET(pd->items), ti)
{
Eina_Position2D child_pos = efl_gfx_entity_position_get(ti->object);
efl_gfx_entity_position_set(ti->object,
EINA_POSITION2D(pos.x - pd->last_pos.x + child_pos.x,
pos.y - pd->last_pos.y + child_pos.y));
}
pd->last_pos = pos;
efl_event_callback_call(ui_table, EFL_PACK_EVENT_LAYOUT_UPDATED, NULL);
}
void
_efl_ui_table_custom_layout(Efl_Ui_Table *ui_table, Efl_Ui_Table_Data *pd)
{
Table_Item *ti;
Item_Calc *items, *item;
Efl_Ui_Container_Item_Hints *hints;
int id = 0, i = 0, count, rows, cols;
int (*_efl_ui_table_item_pos_get[2])(Table_Calc *, Item_Calc *, Eina_Bool);
int (*_efl_ui_table_item_size_get[2])(Table_Calc *, Item_Calc *, Eina_Bool);
Table_Calc table_calc;
Eina_Bool do_free;
count = pd->count;
if (!count)
{
efl_gfx_hint_size_restricted_min_set(ui_table, EINA_SIZE2D(0, 0));
return;
}
if (!pd->full_recalc)
{
_efl_ui_table_layout_simple(ui_table, pd);
return;
}
_efl_ui_container_layout_init(ui_table, table_calc.layout_calc);
pd->last_pos.x = table_calc.layout_calc[0].pos - table_calc.layout_calc[0].margin[0];
pd->last_pos.y = table_calc.layout_calc[1].pos - table_calc.layout_calc[1].margin[0];
table_calc.want[0] = table_calc.want[1] = 0;
table_calc.weight_sum[0] = table_calc.weight_sum[1] = 0;
efl_pack_table_size_get(ui_table, &cols, &rows);
table_calc.cell_calc[0] = alloca(cols * sizeof(Cell_Calc));
table_calc.cell_calc[1] = alloca(rows * sizeof(Cell_Calc));
memset(table_calc.cell_calc[0], 0, cols * sizeof(Cell_Calc));
memset(table_calc.cell_calc[1], 0, rows * sizeof(Cell_Calc));
/* Item_Calc struct is currently 152 bytes.
* this is pretty big to be allocating a huge number of, and we don't want to explode the stack
*/
do_free = count >= 500;
if (do_free)
{
items = malloc(count * sizeof(*items));
EINA_SAFETY_ON_NULL_RETURN(items);
}
else
items = alloca(count * sizeof(*items));
#ifdef DEBUG
memset(items, 0, count * sizeof(*items));
#endif
table_calc.cols = cols;
table_calc.rows = rows;
// scan all items, get their properties, calculate total weight & min size
EINA_INLIST_FOREACH(EINA_INLIST_GET(pd->items), ti)
{
if (((ti->col + ti->col_span) > cols) ||
((ti->row + ti->row_span) > rows))
{
efl_gfx_entity_geometry_set(ti->object, EINA_RECT(9999, 9999, 0, 0));
count--;
continue;
}
item = &items[id++];
item->obj = ti->object;
hints = item->hints;
_efl_ui_container_layout_item_init(item->obj, hints);
if (table_calc.layout_calc[0].fill || pd->homogeneoush)
hints[0].weight = 1;
else if (hints[0].weight < 0)
hints[0].weight = 0;
if (table_calc.layout_calc[1].fill || pd->homogeneousv)
hints[1].weight = 1;
else if (hints[1].weight < 0)
hints[1].weight = 0;
item->cell_index[0] = ti->col;
item->cell_index[1] = ti->row;
item->cell_span[0] = ti->col_span;
item->cell_span[1] = ti->row_span;
int end;
double ispace, iweight;
end = ti->col + ti->col_span;
ispace = hints[0].space / ti->col_span;
iweight = hints[0].weight / ti->col_span;
for (i = ti->col; i < end; i++)
{
table_calc.cell_calc[0][i].occupied = EINA_TRUE;
if (table_calc.cell_calc[0][i].space < ispace)
table_calc.cell_calc[0][i].space = ispace;
if (table_calc.cell_calc[0][i].weight < iweight)
table_calc.cell_calc[0][i].weight = iweight;
}
end = ti->row + ti->row_span;
ispace = hints[1].space / ti->row_span;
iweight = hints[1].weight / ti->row_span;
for (i = ti->row; i < end; i++)
{
table_calc.cell_calc[1][i].occupied = EINA_TRUE;
if (table_calc.cell_calc[1][i].space < ispace)
table_calc.cell_calc[1][i].space = ispace;
if (table_calc.cell_calc[1][i].weight < iweight)
table_calc.cell_calc[1][i].weight = iweight;
}
}
if (pd->homogeneoush)
{
_efl_ui_table_homogeneous_cell_init(&table_calc, 0);
_efl_ui_table_item_pos_get[0] = _efl_ui_table_homogeneous_item_pos_get;
_efl_ui_table_item_size_get[0] = _efl_ui_table_homogeneous_item_size_get;
}
else
{
_efl_ui_table_regular_cell_init(&table_calc, 0);
_efl_ui_table_item_pos_get[0] = _efl_ui_table_regular_item_pos_get;
_efl_ui_table_item_size_get[0] = _efl_ui_table_regular_item_size_get;
}
if (pd->homogeneousv)
{
_efl_ui_table_homogeneous_cell_init(&table_calc, 1);
_efl_ui_table_item_pos_get[1] = _efl_ui_table_homogeneous_item_pos_get;
_efl_ui_table_item_size_get[1] = _efl_ui_table_homogeneous_item_size_get;
}
else
{
_efl_ui_table_regular_cell_init(&table_calc, 1);
_efl_ui_table_item_pos_get[1] = _efl_ui_table_regular_item_pos_get;
_efl_ui_table_item_size_get[1] = _efl_ui_table_regular_item_size_get;
}
for (i = 0; i < count; i++)
{
Eina_Rect space, item_geom;
item = &items[i];
hints = items[i].hints;
space.x = _efl_ui_table_item_pos_get[0](&table_calc, item, 0);
space.y = _efl_ui_table_item_pos_get[1](&table_calc, item, 1);
space.w = _efl_ui_table_item_size_get[0](&table_calc, item, 0);
space.h = _efl_ui_table_item_size_get[1](&table_calc, item, 1);
item_geom.w = hints[0].fill ? space.w : hints[0].min;
item_geom.h = hints[1].fill ? space.h : hints[1].min;
_efl_ui_container_layout_min_max_calc(hints, &item_geom.w, &item_geom.h,
(hints[0].aspect > 0) && (hints[1].aspect > 0));
item_geom.x = space.x + ((space.w - item_geom.w) * hints[0].align)
+ hints[0].margin[0];
item_geom.y = space.y + ((space.h - item_geom.h) * hints[1].align)
+ hints[1].margin[0];
efl_gfx_entity_geometry_set(item->obj, item_geom);
}
table_calc.want[0] += table_calc.layout_calc[0].margin[0]
+ table_calc.layout_calc[0].margin[1]
+ (table_calc.layout_calc[0].pad *
table_calc.cell_calc[0][cols - 1].index);
table_calc.want[1] += table_calc.layout_calc[1].margin[0]
+ table_calc.layout_calc[1].margin[1]
+ (table_calc.layout_calc[1].pad *
table_calc.cell_calc[1][rows - 1].index);
pd->full_recalc = EINA_FALSE;
efl_gfx_hint_size_restricted_min_set(ui_table,
EINA_SIZE2D(table_calc.want[0],
table_calc.want[1]));
efl_event_callback_call(ui_table, EFL_PACK_EVENT_LAYOUT_UPDATED, NULL);
if (do_free) free(items);
}